8b23116624d5f29d96bd29e13fc770b692ea02b6
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9161  * @cfg {Boolean} rowSelection (true|false) default false
9162  * @cfg {Boolean} cellSelection (true|false) default false
9163  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9165  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9166  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9167  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9169  *
9170  * 
9171  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9172  * 
9173  * @constructor
9174  * Create a new Table
9175  * @param {Object} config The config object
9176  */
9177
9178 Roo.bootstrap.Table = function(config)
9179 {
9180     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9181      
9182     // BC...
9183     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9187     
9188     this.view = this; // compat with grid.
9189     
9190     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9191     if (this.sm) {
9192         this.sm.grid = this;
9193         this.selModel = Roo.factory(this.sm, Roo.grid);
9194         this.sm = this.selModel;
9195         this.sm.xmodule = this.xmodule || false;
9196     }
9197     
9198     if (this.cm && typeof(this.cm.config) == 'undefined') {
9199         this.colModel = new Roo.grid.ColumnModel(this.cm);
9200         this.cm = this.colModel;
9201         this.cm.xmodule = this.xmodule || false;
9202     }
9203     if (this.store) {
9204         this.store= Roo.factory(this.store, Roo.data);
9205         this.ds = this.store;
9206         this.ds.xmodule = this.xmodule || false;
9207          
9208     }
9209     if (this.footer && this.store) {
9210         this.footer.dataSource = this.ds;
9211         this.footer = Roo.factory(this.footer);
9212     }
9213     
9214     /** @private */
9215     this.addEvents({
9216         /**
9217          * @event cellclick
9218          * Fires when a cell is clicked
9219          * @param {Roo.bootstrap.Table} this
9220          * @param {Roo.Element} el
9221          * @param {Number} rowIndex
9222          * @param {Number} columnIndex
9223          * @param {Roo.EventObject} e
9224          */
9225         "cellclick" : true,
9226         /**
9227          * @event celldblclick
9228          * Fires when a cell is double clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "celldblclick" : true,
9236         /**
9237          * @event rowclick
9238          * Fires when a row is clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Roo.EventObject} e
9243          */
9244         "rowclick" : true,
9245         /**
9246          * @event rowdblclick
9247          * Fires when a row is double clicked
9248          * @param {Roo.bootstrap.Table} this
9249          * @param {Roo.Element} el
9250          * @param {Number} rowIndex
9251          * @param {Roo.EventObject} e
9252          */
9253         "rowdblclick" : true,
9254         /**
9255          * @event mouseover
9256          * Fires when a mouseover occur
9257          * @param {Roo.bootstrap.Table} this
9258          * @param {Roo.Element} el
9259          * @param {Number} rowIndex
9260          * @param {Number} columnIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "mouseover" : true,
9264         /**
9265          * @event mouseout
9266          * Fires when a mouseout occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseout" : true,
9274         /**
9275          * @event rowclass
9276          * Fires when a row is rendered, so you can change add a style to it.
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9279          */
9280         'rowclass' : true,
9281           /**
9282          * @event rowsrendered
9283          * Fires when all the  rows have been rendered
9284          * @param {Roo.bootstrap.Table} this
9285          */
9286         'rowsrendered' : true,
9287         /**
9288          * @event contextmenu
9289          * The raw contextmenu event for the entire grid.
9290          * @param {Roo.EventObject} e
9291          */
9292         "contextmenu" : true,
9293         /**
9294          * @event rowcontextmenu
9295          * Fires when a row is right clicked
9296          * @param {Roo.bootstrap.Table} this
9297          * @param {Number} rowIndex
9298          * @param {Roo.EventObject} e
9299          */
9300         "rowcontextmenu" : true,
9301         /**
9302          * @event cellcontextmenu
9303          * Fires when a cell is right clicked
9304          * @param {Roo.bootstrap.Table} this
9305          * @param {Number} rowIndex
9306          * @param {Number} cellIndex
9307          * @param {Roo.EventObject} e
9308          */
9309          "cellcontextmenu" : true,
9310          /**
9311          * @event headercontextmenu
9312          * Fires when a header is right clicked
9313          * @param {Roo.bootstrap.Table} this
9314          * @param {Number} columnIndex
9315          * @param {Roo.EventObject} e
9316          */
9317         "headercontextmenu" : true,
9318         /**
9319          * @event mousedown
9320          * The raw mousedown event for the entire grid.
9321          * @param {Roo.EventObject} e
9322          */
9323         "mousedown" : true
9324         
9325     });
9326 };
9327
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9329     
9330     cls: false,
9331     
9332     empty_results : '',
9333     striped : false,
9334     scrollBody : false,
9335     bordered: false,
9336     hover:  false,
9337     condensed : false,
9338     responsive : false,
9339     sm : false,
9340     cm : false,
9341     store : false,
9342     loadMask : false,
9343     footerShow : true,
9344     headerShow : true,
9345     enableColumnResize: true,
9346     disableAutoSize: false,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         if(this.disableAutoSize) {
9509             return;
9510         }
9511         
9512         var cm = this.cm, styles = [];
9513         this.CSS.removeStyleSheet(this.id + '-cssrules');
9514         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9515         // we can honour xs/sm/md/xl  as widths...
9516         // we first have to decide what widht we are currently at...
9517         var sz = Roo.getGridSize();
9518         
9519         var total = 0;
9520         var last = -1;
9521         var cols = []; // visable cols.
9522         var total_abs = 0;
9523         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9524             var w = cm.getColumnWidth(i, false);
9525             if(cm.isHidden(i)){
9526                 cols.push( { rel : false, abs : 0 });
9527                 continue;
9528             }
9529             if (w !== false) {
9530                 cols.push( { rel : false, abs : w });
9531                 total_abs += w;
9532                 last = i; // not really..
9533                 continue;
9534             }
9535             var w = cm.getColumnWidth(i, sz);
9536             if (w > 0) {
9537                 last = i
9538             }
9539             total += w;
9540             cols.push( { rel : w, abs : false });
9541         }
9542         
9543         var avail = this.bodyEl.dom.clientWidth - total_abs;
9544         
9545         var unitWidth = Math.floor(avail / total);
9546         var rem = avail - (unitWidth * total);
9547         
9548         var hidden, width, pos = 0 , splithide , left;
9549         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9550             
9551             hidden = 'display:none;';
9552             left = '';
9553             width  = 'width:0px;';
9554             splithide = '';
9555             if(!cm.isHidden(i)){
9556                 hidden = '';
9557                 
9558                 
9559                 // we can honour xs/sm/md/xl ?
9560                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9561                 if (w===0) {
9562                     hidden = 'display:none;';
9563                 }
9564                 // width should return a small number...
9565                 if (i == last) {
9566                     w+=rem; // add the remaining with..
9567                 }
9568                 pos += w;
9569                 left = "left:" + (pos -4) + "px;";
9570                 width = "width:" + w+ "px;";
9571                 
9572             }
9573             if (this.responsive) {
9574                 width = '';
9575                 left = '';
9576                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9577                 splithide = 'display: none;';
9578             }
9579             
9580             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581             if (this.headEl) {
9582                 if (i == last) {
9583                     splithide = 'display:none;';
9584                 }
9585                 
9586                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9587                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9588                             // this is the popover version..
9589                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9590                 );
9591             }
9592             
9593         }
9594         //Roo.log(styles.join(''));
9595         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9596         
9597     },
9598     
9599     
9600     
9601     onContextMenu : function(e, t)
9602     {
9603         this.processEvent("contextmenu", e);
9604     },
9605     
9606     processEvent : function(name, e)
9607     {
9608         if (name != 'touchstart' ) {
9609             this.fireEvent(name, e);    
9610         }
9611         
9612         var t = e.getTarget();
9613         
9614         var cell = Roo.get(t);
9615         
9616         if(!cell){
9617             return;
9618         }
9619         
9620         if(cell.findParent('tfoot', false, true)){
9621             return;
9622         }
9623         
9624         if(cell.findParent('thead', false, true)){
9625             
9626             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9627                 cell = Roo.get(t).findParent('th', false, true);
9628                 if (!cell) {
9629                     Roo.log("failed to find th in thead?");
9630                     Roo.log(e.getTarget());
9631                     return;
9632                 }
9633             }
9634             
9635             var cellIndex = cell.dom.cellIndex;
9636             
9637             var ename = name == 'touchstart' ? 'click' : name;
9638             this.fireEvent("header" + ename, this, cellIndex, e);
9639             
9640             return;
9641         }
9642         
9643         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9644             cell = Roo.get(t).findParent('td', false, true);
9645             if (!cell) {
9646                 Roo.log("failed to find th in tbody?");
9647                 Roo.log(e.getTarget());
9648                 return;
9649             }
9650         }
9651         
9652         var row = cell.findParent('tr', false, true);
9653         var cellIndex = cell.dom.cellIndex;
9654         var rowIndex = row.dom.rowIndex - 1;
9655         
9656         if(row !== false){
9657             
9658             this.fireEvent("row" + name, this, rowIndex, e);
9659             
9660             if(cell !== false){
9661             
9662                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9663             }
9664         }
9665         
9666     },
9667     
9668     onMouseover : function(e, el)
9669     {
9670         var cell = Roo.get(el);
9671         
9672         if(!cell){
9673             return;
9674         }
9675         
9676         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9677             cell = cell.findParent('td', false, true);
9678         }
9679         
9680         var row = cell.findParent('tr', false, true);
9681         var cellIndex = cell.dom.cellIndex;
9682         var rowIndex = row.dom.rowIndex - 1; // start from 0
9683         
9684         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9685         
9686     },
9687     
9688     onMouseout : function(e, el)
9689     {
9690         var cell = Roo.get(el);
9691         
9692         if(!cell){
9693             return;
9694         }
9695         
9696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697             cell = cell.findParent('td', false, true);
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         var cellIndex = cell.dom.cellIndex;
9702         var rowIndex = row.dom.rowIndex - 1; // start from 0
9703         
9704         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9705         
9706     },
9707     
9708     onClick : function(e, el)
9709     {
9710         var cell = Roo.get(el);
9711         
9712         if(!cell || (!this.cellSelection && !this.rowSelection)){
9713             return;
9714         }
9715         
9716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717             cell = cell.findParent('td', false, true);
9718         }
9719         
9720         if(!cell || typeof(cell) == 'undefined'){
9721             return;
9722         }
9723         
9724         var row = cell.findParent('tr', false, true);
9725         
9726         if(!row || typeof(row) == 'undefined'){
9727             return;
9728         }
9729         
9730         var cellIndex = cell.dom.cellIndex;
9731         var rowIndex = this.getRowIndex(row);
9732         
9733         // why??? - should these not be based on SelectionModel?
9734         //if(this.cellSelection){
9735             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736         //}
9737         
9738         //if(this.rowSelection){
9739             this.fireEvent('rowclick', this, row, rowIndex, e);
9740         //}
9741          
9742     },
9743         
9744     onDblClick : function(e,el)
9745     {
9746         var cell = Roo.get(el);
9747         
9748         if(!cell || (!this.cellSelection && !this.rowSelection)){
9749             return;
9750         }
9751         
9752         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9753             cell = cell.findParent('td', false, true);
9754         }
9755         
9756         if(!cell || typeof(cell) == 'undefined'){
9757             return;
9758         }
9759         
9760         var row = cell.findParent('tr', false, true);
9761         
9762         if(!row || typeof(row) == 'undefined'){
9763             return;
9764         }
9765         
9766         var cellIndex = cell.dom.cellIndex;
9767         var rowIndex = this.getRowIndex(row);
9768         
9769         if(this.cellSelection){
9770             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771         }
9772         
9773         if(this.rowSelection){
9774             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775         }
9776     },
9777     findRowIndex : function(el)
9778     {
9779         var cell = Roo.get(el);
9780         if(!cell) {
9781             return false;
9782         }
9783         var row = cell.findParent('tr', false, true);
9784         
9785         if(!row || typeof(row) == 'undefined'){
9786             return false;
9787         }
9788         return this.getRowIndex(row);
9789     },
9790     sort : function(e,el)
9791     {
9792         var col = Roo.get(el);
9793         
9794         if(!col.hasClass('sortable')){
9795             return;
9796         }
9797         
9798         var sort = col.attr('sort');
9799         var dir = 'ASC';
9800         
9801         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9802             dir = 'DESC';
9803         }
9804         
9805         this.store.sortInfo = {field : sort, direction : dir};
9806         
9807         if (this.footer) {
9808             Roo.log("calling footer first");
9809             this.footer.onClick('first');
9810         } else {
9811         
9812             this.store.load({ params : { start : 0 } });
9813         }
9814     },
9815     
9816     renderHeader : function()
9817     {
9818         var header = {
9819             tag: 'thead',
9820             cn : []
9821         };
9822         
9823         var cm = this.cm;
9824         this.totalWidth = 0;
9825         
9826         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9827             
9828             var config = cm.config[i];
9829             
9830             var c = {
9831                 tag: 'th',
9832                 cls : 'x-hcol-' + i,
9833                 style : '',
9834                 
9835                 html: cm.getColumnHeader(i)
9836             };
9837             
9838             var tooltip = cm.getColumnTooltip(i);
9839             if (tooltip) {
9840                 c.tooltip = tooltip;
9841             }
9842             
9843             
9844             var hh = '';
9845             
9846             if(typeof(config.sortable) != 'undefined' && config.sortable){
9847                 c.cls += ' sortable';
9848                 c.html = '<i class="fa"></i>' + c.html;
9849             }
9850             
9851             // could use BS4 hidden-..-down 
9852             
9853             if(typeof(config.lgHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.mdHeader) != 'undefined'){
9858                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859             }
9860             
9861             if(typeof(config.smHeader) != 'undefined'){
9862                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863             }
9864             
9865             if(typeof(config.xsHeader) != 'undefined'){
9866                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9867             }
9868             
9869             if(hh.length){
9870                 c.html = hh;
9871             }
9872             
9873             if(typeof(config.tooltip) != 'undefined'){
9874                 c.tooltip = config.tooltip;
9875             }
9876             
9877             if(typeof(config.colspan) != 'undefined'){
9878                 c.colspan = config.colspan;
9879             }
9880             
9881             // hidden is handled by CSS now
9882             
9883             if(typeof(config.dataIndex) != 'undefined'){
9884                 c.sort = config.dataIndex;
9885             }
9886             
9887            
9888             
9889             if(typeof(config.align) != 'undefined' && config.align.length){
9890                 c.style += ' text-align:' + config.align + ';';
9891             }
9892             
9893             /* width is done in CSS
9894              *if(typeof(config.width) != 'undefined'){
9895                 c.style += ' width:' + config.width + 'px;';
9896                 this.totalWidth += config.width;
9897             } else {
9898                 this.totalWidth += 100; // assume minimum of 100 per column?
9899             }
9900             */
9901             
9902             if(typeof(config.cls) != 'undefined'){
9903                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9904             }
9905             // this is the bit that doesnt reall work at all...
9906             
9907             if (this.responsive) {
9908                  
9909             
9910                 ['xs','sm','md','lg'].map(function(size){
9911                     
9912                     if(typeof(config[size]) == 'undefined'){
9913                         return;
9914                     }
9915                      
9916                     if (!config[size]) { // 0 = hidden
9917                         // BS 4 '0' is treated as hide that column and below.
9918                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9919                         return;
9920                     }
9921                     
9922                     c.cls += ' col-' + size + '-' + config[size] + (
9923                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9924                     );
9925                     
9926                     
9927                 });
9928             }
9929             // at the end?
9930             
9931             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9932             
9933             
9934             
9935             
9936             header.cn.push(c)
9937         }
9938         
9939         return header;
9940     },
9941     
9942     renderBody : function()
9943     {
9944         var body = {
9945             tag: 'tbody',
9946             cn : [
9947                 {
9948                     tag: 'tr',
9949                     cn : [
9950                         {
9951                             tag : 'td',
9952                             colspan :  this.cm.getColumnCount()
9953                         }
9954                     ]
9955                 }
9956             ]
9957         };
9958         
9959         return body;
9960     },
9961     
9962     renderFooter : function()
9963     {
9964         var footer = {
9965             tag: 'tfoot',
9966             cn : [
9967                 {
9968                     tag: 'tr',
9969                     cn : [
9970                         {
9971                             tag : 'td',
9972                             colspan :  this.cm.getColumnCount()
9973                         }
9974                     ]
9975                 }
9976             ]
9977         };
9978         
9979         return footer;
9980     },
9981     
9982     
9983     
9984     onLoad : function()
9985     {
9986 //        Roo.log('ds onload');
9987         this.clear();
9988         
9989         var _this = this;
9990         var cm = this.cm;
9991         var ds = this.store;
9992         
9993         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9994             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9995             if (_this.store.sortInfo) {
9996                     
9997                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9998                     e.select('i', true).addClass(['fa-arrow-up']);
9999                 }
10000                 
10001                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10002                     e.select('i', true).addClass(['fa-arrow-down']);
10003                 }
10004             }
10005         });
10006         
10007         var tbody =  this.bodyEl;
10008               
10009         if(ds.getCount() > 0){
10010             ds.data.each(function(d,rowIndex){
10011                 var row =  this.renderRow(cm, ds, rowIndex);
10012                 
10013                 tbody.createChild(row);
10014                 
10015                 var _this = this;
10016                 
10017                 if(row.cellObjects.length){
10018                     Roo.each(row.cellObjects, function(r){
10019                         _this.renderCellObject(r);
10020                     })
10021                 }
10022                 
10023             }, this);
10024         } else if (this.empty_results.length) {
10025             this.el.mask(this.empty_results, 'no-spinner');
10026         }
10027         
10028         var tfoot = this.el.select('tfoot', true).first();
10029         
10030         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10031             
10032             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10033             
10034             var total = this.ds.getTotalCount();
10035             
10036             if(this.footer.pageSize < total){
10037                 this.mainFoot.show();
10038             }
10039         }
10040         
10041         Roo.each(this.el.select('tbody td', true).elements, function(e){
10042             e.on('mouseover', _this.onMouseover, _this);
10043         });
10044         
10045         Roo.each(this.el.select('tbody td', true).elements, function(e){
10046             e.on('mouseout', _this.onMouseout, _this);
10047         });
10048         this.fireEvent('rowsrendered', this);
10049         
10050         this.autoSize();
10051         
10052         this.initCSS(); /// resize cols
10053
10054         
10055     },
10056     
10057     
10058     onUpdate : function(ds,record)
10059     {
10060         this.refreshRow(record);
10061         this.autoSize();
10062     },
10063     
10064     onRemove : function(ds, record, index, isUpdate){
10065         if(isUpdate !== true){
10066             this.fireEvent("beforerowremoved", this, index, record);
10067         }
10068         var bt = this.bodyEl.dom;
10069         
10070         var rows = this.el.select('tbody > tr', true).elements;
10071         
10072         if(typeof(rows[index]) != 'undefined'){
10073             bt.removeChild(rows[index].dom);
10074         }
10075         
10076 //        if(bt.rows[index]){
10077 //            bt.removeChild(bt.rows[index]);
10078 //        }
10079         
10080         if(isUpdate !== true){
10081             //this.stripeRows(index);
10082             //this.syncRowHeights(index, index);
10083             //this.layout();
10084             this.fireEvent("rowremoved", this, index, record);
10085         }
10086     },
10087     
10088     onAdd : function(ds, records, rowIndex)
10089     {
10090         //Roo.log('on Add called');
10091         // - note this does not handle multiple adding very well..
10092         var bt = this.bodyEl.dom;
10093         for (var i =0 ; i < records.length;i++) {
10094             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10095             //Roo.log(records[i]);
10096             //Roo.log(this.store.getAt(rowIndex+i));
10097             this.insertRow(this.store, rowIndex + i, false);
10098             return;
10099         }
10100         
10101     },
10102     
10103     
10104     refreshRow : function(record){
10105         var ds = this.store, index;
10106         if(typeof record == 'number'){
10107             index = record;
10108             record = ds.getAt(index);
10109         }else{
10110             index = ds.indexOf(record);
10111             if (index < 0) {
10112                 return; // should not happen - but seems to 
10113             }
10114         }
10115         this.insertRow(ds, index, true);
10116         this.autoSize();
10117         this.onRemove(ds, record, index+1, true);
10118         this.autoSize();
10119         //this.syncRowHeights(index, index);
10120         //this.layout();
10121         this.fireEvent("rowupdated", this, index, record);
10122     },
10123     // private - called by RowSelection
10124     onRowSelect : function(rowIndex){
10125         var row = this.getRowDom(rowIndex);
10126         row.addClass(['bg-info','info']);
10127     },
10128     // private - called by RowSelection
10129     onRowDeselect : function(rowIndex)
10130     {
10131         if (rowIndex < 0) {
10132             return;
10133         }
10134         var row = this.getRowDom(rowIndex);
10135         row.removeClass(['bg-info','info']);
10136     },
10137       /**
10138      * Focuses the specified row.
10139      * @param {Number} row The row index
10140      */
10141     focusRow : function(row)
10142     {
10143         //Roo.log('GridView.focusRow');
10144         var x = this.bodyEl.dom.scrollLeft;
10145         this.focusCell(row, 0, false);
10146         this.bodyEl.dom.scrollLeft = x;
10147
10148     },
10149      /**
10150      * Focuses the specified cell.
10151      * @param {Number} row The row index
10152      * @param {Number} col The column index
10153      * @param {Boolean} hscroll false to disable horizontal scrolling
10154      */
10155     focusCell : function(row, col, hscroll)
10156     {
10157         //Roo.log('GridView.focusCell');
10158         var el = this.ensureVisible(row, col, hscroll);
10159         // not sure what focusEL achives = it's a <a> pos relative 
10160         //this.focusEl.alignTo(el, "tl-tl");
10161         //if(Roo.isGecko){
10162         //    this.focusEl.focus();
10163         //}else{
10164         //    this.focusEl.focus.defer(1, this.focusEl);
10165         //}
10166     },
10167     
10168      /**
10169      * Scrolls the specified cell into view
10170      * @param {Number} row The row index
10171      * @param {Number} col The column index
10172      * @param {Boolean} hscroll false to disable horizontal scrolling
10173      */
10174     ensureVisible : function(row, col, hscroll)
10175     {
10176         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10177         //return null; //disable for testing.
10178         if(typeof row != "number"){
10179             row = row.rowIndex;
10180         }
10181         if(row < 0 && row >= this.ds.getCount()){
10182             return  null;
10183         }
10184         col = (col !== undefined ? col : 0);
10185         var cm = this.cm;
10186         while(cm.isHidden(col)){
10187             col++;
10188         }
10189
10190         var el = this.getCellDom(row, col);
10191         if(!el){
10192             return null;
10193         }
10194         var c = this.bodyEl.dom;
10195
10196         var ctop = parseInt(el.offsetTop, 10);
10197         var cleft = parseInt(el.offsetLeft, 10);
10198         var cbot = ctop + el.offsetHeight;
10199         var cright = cleft + el.offsetWidth;
10200
10201         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10202         var ch = 0; //?? header is not withing the area?
10203         var stop = parseInt(c.scrollTop, 10);
10204         var sleft = parseInt(c.scrollLeft, 10);
10205         var sbot = stop + ch;
10206         var sright = sleft + c.clientWidth;
10207         /*
10208         Roo.log('GridView.ensureVisible:' +
10209                 ' ctop:' + ctop +
10210                 ' c.clientHeight:' + c.clientHeight +
10211                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10212                 ' stop:' + stop +
10213                 ' cbot:' + cbot +
10214                 ' sbot:' + sbot +
10215                 ' ch:' + ch  
10216                 );
10217         */
10218         if(ctop < stop){
10219             c.scrollTop = ctop;
10220             //Roo.log("set scrolltop to ctop DISABLE?");
10221         }else if(cbot > sbot){
10222             //Roo.log("set scrolltop to cbot-ch");
10223             c.scrollTop = cbot-ch;
10224         }
10225
10226         if(hscroll !== false){
10227             if(cleft < sleft){
10228                 c.scrollLeft = cleft;
10229             }else if(cright > sright){
10230                 c.scrollLeft = cright-c.clientWidth;
10231             }
10232         }
10233
10234         return el;
10235     },
10236     
10237     
10238     insertRow : function(dm, rowIndex, isUpdate){
10239         
10240         if(!isUpdate){
10241             this.fireEvent("beforerowsinserted", this, rowIndex);
10242         }
10243             //var s = this.getScrollState();
10244         var row = this.renderRow(this.cm, this.store, rowIndex);
10245         // insert before rowIndex..
10246         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10247         
10248         var _this = this;
10249                 
10250         if(row.cellObjects.length){
10251             Roo.each(row.cellObjects, function(r){
10252                 _this.renderCellObject(r);
10253             })
10254         }
10255             
10256         if(!isUpdate){
10257             this.fireEvent("rowsinserted", this, rowIndex);
10258             //this.syncRowHeights(firstRow, lastRow);
10259             //this.stripeRows(firstRow);
10260             //this.layout();
10261         }
10262         
10263     },
10264     
10265     
10266     getRowDom : function(rowIndex)
10267     {
10268         var rows = this.el.select('tbody > tr', true).elements;
10269         
10270         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271         
10272     },
10273     getCellDom : function(rowIndex, colIndex)
10274     {
10275         var row = this.getRowDom(rowIndex);
10276         if (row === false) {
10277             return false;
10278         }
10279         var cols = row.select('td', true).elements;
10280         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10281         
10282     },
10283     
10284     // returns the object tree for a tr..
10285   
10286     
10287     renderRow : function(cm, ds, rowIndex) 
10288     {
10289         var d = ds.getAt(rowIndex);
10290         
10291         var row = {
10292             tag : 'tr',
10293             cls : 'x-row-' + rowIndex,
10294             cn : []
10295         };
10296             
10297         var cellObjects = [];
10298         
10299         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10300             var config = cm.config[i];
10301             
10302             var renderer = cm.getRenderer(i);
10303             var value = '';
10304             var id = false;
10305             
10306             if(typeof(renderer) !== 'undefined'){
10307                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10308             }
10309             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10310             // and are rendered into the cells after the row is rendered - using the id for the element.
10311             
10312             if(typeof(value) === 'object'){
10313                 id = Roo.id();
10314                 cellObjects.push({
10315                     container : id,
10316                     cfg : value 
10317                 })
10318             }
10319             
10320             var rowcfg = {
10321                 record: d,
10322                 rowIndex : rowIndex,
10323                 colIndex : i,
10324                 rowClass : ''
10325             };
10326
10327             this.fireEvent('rowclass', this, rowcfg);
10328             
10329             var td = {
10330                 tag: 'td',
10331                 // this might end up displaying HTML?
10332                 // this is too messy... - better to only do it on columsn you know are going to be too long
10333                 //tooltip : (typeof(value) === 'object') ? '' : value,
10334                 cls : rowcfg.rowClass + ' x-col-' + i,
10335                 style: '',
10336                 html: (typeof(value) === 'object') ? '' : value
10337             };
10338             
10339             if (id) {
10340                 td.id = id;
10341             }
10342             
10343             if(typeof(config.colspan) != 'undefined'){
10344                 td.colspan = config.colspan;
10345             }
10346             
10347             
10348             
10349             if(typeof(config.align) != 'undefined' && config.align.length){
10350                 td.style += ' text-align:' + config.align + ';';
10351             }
10352             if(typeof(config.valign) != 'undefined' && config.valign.length){
10353                 td.style += ' vertical-align:' + config.valign + ';';
10354             }
10355             /*
10356             if(typeof(config.width) != 'undefined'){
10357                 td.style += ' width:' +  config.width + 'px;';
10358             }
10359             */
10360             
10361             if(typeof(config.cursor) != 'undefined'){
10362                 td.style += ' cursor:' +  config.cursor + ';';
10363             }
10364             
10365             if(typeof(config.cls) != 'undefined'){
10366                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10367             }
10368             if (this.responsive) {
10369                 ['xs','sm','md','lg'].map(function(size){
10370                     
10371                     if(typeof(config[size]) == 'undefined'){
10372                         return;
10373                     }
10374                     
10375                     
10376                       
10377                     if (!config[size]) { // 0 = hidden
10378                         // BS 4 '0' is treated as hide that column and below.
10379                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10380                         return;
10381                     }
10382                     
10383                     td.cls += ' col-' + size + '-' + config[size] + (
10384                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10385                     );
10386                      
10387     
10388                 });
10389             }
10390             row.cn.push(td);
10391            
10392         }
10393         
10394         row.cellObjects = cellObjects;
10395         
10396         return row;
10397           
10398     },
10399     
10400     
10401     
10402     onBeforeLoad : function()
10403     {
10404         this.el.unmask(); // if needed.
10405     },
10406      /**
10407      * Remove all rows
10408      */
10409     clear : function()
10410     {
10411         this.el.select('tbody', true).first().dom.innerHTML = '';
10412     },
10413     /**
10414      * Show or hide a row.
10415      * @param {Number} rowIndex to show or hide
10416      * @param {Boolean} state hide
10417      */
10418     setRowVisibility : function(rowIndex, state)
10419     {
10420         var bt = this.bodyEl.dom;
10421         
10422         var rows = this.el.select('tbody > tr', true).elements;
10423         
10424         if(typeof(rows[rowIndex]) == 'undefined'){
10425             return;
10426         }
10427         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10428         
10429     },
10430     
10431     
10432     getSelectionModel : function(){
10433         if(!this.selModel){
10434             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10435         }
10436         return this.selModel;
10437     },
10438     /*
10439      * Render the Roo.bootstrap object from renderder
10440      */
10441     renderCellObject : function(r)
10442     {
10443         var _this = this;
10444         
10445         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10446         
10447         var t = r.cfg.render(r.container);
10448         
10449         if(r.cfg.cn){
10450             Roo.each(r.cfg.cn, function(c){
10451                 var child = {
10452                     container: t.getChildContainer(),
10453                     cfg: c
10454                 };
10455                 _this.renderCellObject(child);
10456             })
10457         }
10458     },
10459     /**
10460      * get the Row Index from a dom element.
10461      * @param {Roo.Element} row The row to look for
10462      * @returns {Number} the row
10463      */
10464     getRowIndex : function(row)
10465     {
10466         var rowIndex = -1;
10467         
10468         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10469             if(el != row){
10470                 return;
10471             }
10472             
10473             rowIndex = index;
10474         });
10475         
10476         return rowIndex;
10477     },
10478     /**
10479      * get the header TH element for columnIndex
10480      * @param {Number} columnIndex
10481      * @returns {Roo.Element}
10482      */
10483     getHeaderIndex: function(colIndex)
10484     {
10485         var cols = this.headEl.select('th', true).elements;
10486         return cols[colIndex]; 
10487     },
10488     /**
10489      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10490      * @param {domElement} cell to look for
10491      * @returns {Number} the column
10492      */
10493     getCellIndex : function(cell)
10494     {
10495         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10496         if(id){
10497             return parseInt(id[1], 10);
10498         }
10499         return 0;
10500     },
10501      /**
10502      * Returns the grid's underlying element = used by panel.Grid
10503      * @return {Element} The element
10504      */
10505     getGridEl : function(){
10506         return this.el;
10507     },
10508      /**
10509      * Forces a resize - used by panel.Grid
10510      * @return {Element} The element
10511      */
10512     autoSize : function()
10513     {
10514         if(this.disableAutoSize) {
10515             return;
10516         }
10517         //var ctr = Roo.get(this.container.dom.parentElement);
10518         var ctr = Roo.get(this.el.dom);
10519         
10520         var thd = this.getGridEl().select('thead',true).first();
10521         var tbd = this.getGridEl().select('tbody', true).first();
10522         var tfd = this.getGridEl().select('tfoot', true).first();
10523         
10524         var cw = ctr.getWidth();
10525         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10526         
10527         if (tbd) {
10528             
10529             tbd.setWidth(ctr.getWidth());
10530             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10531             // this needs fixing for various usage - currently only hydra job advers I think..
10532             //tdb.setHeight(
10533             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10534             //); 
10535             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10536             cw -= barsize;
10537         }
10538         cw = Math.max(cw, this.totalWidth);
10539         this.getGridEl().select('tbody tr',true).setWidth(cw);
10540         this.initCSS();
10541         
10542         // resize 'expandable coloumn?
10543         
10544         return; // we doe not have a view in this design..
10545         
10546     },
10547     onBodyScroll: function()
10548     {
10549         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10550         if(this.headEl){
10551             this.headEl.setStyle({
10552                 'position' : 'relative',
10553                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554             });
10555         }
10556         
10557         if(this.lazyLoad){
10558             
10559             var scrollHeight = this.bodyEl.dom.scrollHeight;
10560             
10561             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10562             
10563             var height = this.bodyEl.getHeight();
10564             
10565             if(scrollHeight - height == scrollTop) {
10566                 
10567                 var total = this.ds.getTotalCount();
10568                 
10569                 if(this.footer.cursor + this.footer.pageSize < total){
10570                     
10571                     this.footer.ds.load({
10572                         params : {
10573                             start : this.footer.cursor + this.footer.pageSize,
10574                             limit : this.footer.pageSize
10575                         },
10576                         add : true
10577                     });
10578                 }
10579             }
10580             
10581         }
10582     },
10583     onColumnSplitterMoved : function(i, diff)
10584     {
10585         this.userResized = true;
10586         
10587         var cm = this.colModel;
10588         
10589         var w = this.getHeaderIndex(i).getWidth() + diff;
10590         
10591         
10592         cm.setColumnWidth(i, w, true);
10593         this.initCSS();
10594         //var cid = cm.getColumnId(i); << not used in this version?
10595        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10596         
10597         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10598         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10599         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10600 */
10601         //this.updateSplitters();
10602         //this.layout(); << ??
10603         this.fireEvent("columnresize", i, w);
10604     },
10605     onHeaderChange : function()
10606     {
10607         var header = this.renderHeader();
10608         var table = this.el.select('table', true).first();
10609         
10610         this.headEl.remove();
10611         this.headEl = table.createChild(header, this.bodyEl, false);
10612         
10613         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10614             e.on('click', this.sort, this);
10615         }, this);
10616         
10617         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10618             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10619         }
10620         
10621     },
10622     
10623     onHiddenChange : function(colModel, colIndex, hidden)
10624     {
10625         /*
10626         this.cm.setHidden()
10627         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10628         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10629         
10630         this.CSS.updateRule(thSelector, "display", "");
10631         this.CSS.updateRule(tdSelector, "display", "");
10632         
10633         if(hidden){
10634             this.CSS.updateRule(thSelector, "display", "none");
10635             this.CSS.updateRule(tdSelector, "display", "none");
10636         }
10637         */
10638         // onload calls initCSS()
10639         this.onHeaderChange();
10640         this.onLoad();
10641     },
10642     
10643     setColumnWidth: function(col_index, width)
10644     {
10645         // width = "md-2 xs-2..."
10646         if(!this.colModel.config[col_index]) {
10647             return;
10648         }
10649         
10650         var w = width.split(" ");
10651         
10652         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10653         
10654         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10655         
10656         
10657         for(var j = 0; j < w.length; j++) {
10658             
10659             if(!w[j]) {
10660                 continue;
10661             }
10662             
10663             var size_cls = w[j].split("-");
10664             
10665             if(!Number.isInteger(size_cls[1] * 1)) {
10666                 continue;
10667             }
10668             
10669             if(!this.colModel.config[col_index][size_cls[0]]) {
10670                 continue;
10671             }
10672             
10673             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10674                 continue;
10675             }
10676             
10677             h_row[0].classList.replace(
10678                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10679                 "col-"+size_cls[0]+"-"+size_cls[1]
10680             );
10681             
10682             for(var i = 0; i < rows.length; i++) {
10683                 
10684                 var size_cls = w[j].split("-");
10685                 
10686                 if(!Number.isInteger(size_cls[1] * 1)) {
10687                     continue;
10688                 }
10689                 
10690                 if(!this.colModel.config[col_index][size_cls[0]]) {
10691                     continue;
10692                 }
10693                 
10694                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10695                     continue;
10696                 }
10697                 
10698                 rows[i].classList.replace(
10699                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10700                     "col-"+size_cls[0]+"-"+size_cls[1]
10701                 );
10702             }
10703             
10704             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10705         }
10706     }
10707 });
10708
10709 // currently only used to find the split on drag.. 
10710 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10711
10712 /**
10713  * @depricated
10714 */
10715 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10716 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10717 /*
10718  * - LGPL
10719  *
10720  * table cell
10721  * 
10722  */
10723
10724 /**
10725  * @class Roo.bootstrap.TableCell
10726  * @extends Roo.bootstrap.Component
10727  * @children Roo.bootstrap.Component
10728  * @parent Roo.bootstrap.TableRow
10729  * Bootstrap TableCell class
10730  * 
10731  * @cfg {String} html cell contain text
10732  * @cfg {String} cls cell class
10733  * @cfg {String} tag cell tag (td|th) default td
10734  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10735  * @cfg {String} align Aligns the content in a cell
10736  * @cfg {String} axis Categorizes cells
10737  * @cfg {String} bgcolor Specifies the background color of a cell
10738  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10739  * @cfg {Number} colspan Specifies the number of columns a cell should span
10740  * @cfg {String} headers Specifies one or more header cells a cell is related to
10741  * @cfg {Number} height Sets the height of a cell
10742  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10743  * @cfg {Number} rowspan Sets the number of rows a cell should span
10744  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10745  * @cfg {String} valign Vertical aligns the content in a cell
10746  * @cfg {Number} width Specifies the width of a cell
10747  * 
10748  * @constructor
10749  * Create a new TableCell
10750  * @param {Object} config The config object
10751  */
10752
10753 Roo.bootstrap.TableCell = function(config){
10754     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10755 };
10756
10757 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10758     
10759     html: false,
10760     cls: false,
10761     tag: false,
10762     abbr: false,
10763     align: false,
10764     axis: false,
10765     bgcolor: false,
10766     charoff: false,
10767     colspan: false,
10768     headers: false,
10769     height: false,
10770     nowrap: false,
10771     rowspan: false,
10772     scope: false,
10773     valign: false,
10774     width: false,
10775     
10776     
10777     getAutoCreate : function(){
10778         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10779         
10780         cfg = {
10781             tag: 'td'
10782         };
10783         
10784         if(this.tag){
10785             cfg.tag = this.tag;
10786         }
10787         
10788         if (this.html) {
10789             cfg.html=this.html
10790         }
10791         if (this.cls) {
10792             cfg.cls=this.cls
10793         }
10794         if (this.abbr) {
10795             cfg.abbr=this.abbr
10796         }
10797         if (this.align) {
10798             cfg.align=this.align
10799         }
10800         if (this.axis) {
10801             cfg.axis=this.axis
10802         }
10803         if (this.bgcolor) {
10804             cfg.bgcolor=this.bgcolor
10805         }
10806         if (this.charoff) {
10807             cfg.charoff=this.charoff
10808         }
10809         if (this.colspan) {
10810             cfg.colspan=this.colspan
10811         }
10812         if (this.headers) {
10813             cfg.headers=this.headers
10814         }
10815         if (this.height) {
10816             cfg.height=this.height
10817         }
10818         if (this.nowrap) {
10819             cfg.nowrap=this.nowrap
10820         }
10821         if (this.rowspan) {
10822             cfg.rowspan=this.rowspan
10823         }
10824         if (this.scope) {
10825             cfg.scope=this.scope
10826         }
10827         if (this.valign) {
10828             cfg.valign=this.valign
10829         }
10830         if (this.width) {
10831             cfg.width=this.width
10832         }
10833         
10834         
10835         return cfg;
10836     }
10837    
10838 });
10839
10840  
10841
10842  /*
10843  * - LGPL
10844  *
10845  * table row
10846  * 
10847  */
10848
10849 /**
10850  * @class Roo.bootstrap.TableRow
10851  * @extends Roo.bootstrap.Component
10852  * @children Roo.bootstrap.TableCell
10853  * @parent Roo.bootstrap.TableBody
10854  * Bootstrap TableRow class
10855  * @cfg {String} cls row class
10856  * @cfg {String} align Aligns the content in a table row
10857  * @cfg {String} bgcolor Specifies a background color for a table row
10858  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10859  * @cfg {String} valign Vertical aligns the content in a table row
10860  * 
10861  * @constructor
10862  * Create a new TableRow
10863  * @param {Object} config The config object
10864  */
10865
10866 Roo.bootstrap.TableRow = function(config){
10867     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10868 };
10869
10870 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10871     
10872     cls: false,
10873     align: false,
10874     bgcolor: false,
10875     charoff: false,
10876     valign: false,
10877     
10878     getAutoCreate : function(){
10879         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10880         
10881         cfg = {
10882             tag: 'tr'
10883         };
10884             
10885         if(this.cls){
10886             cfg.cls = this.cls;
10887         }
10888         if(this.align){
10889             cfg.align = this.align;
10890         }
10891         if(this.bgcolor){
10892             cfg.bgcolor = this.bgcolor;
10893         }
10894         if(this.charoff){
10895             cfg.charoff = this.charoff;
10896         }
10897         if(this.valign){
10898             cfg.valign = this.valign;
10899         }
10900         
10901         return cfg;
10902     }
10903    
10904 });
10905
10906  
10907
10908  /*
10909  * - LGPL
10910  *
10911  * table body
10912  * 
10913  */
10914
10915 /**
10916  * @class Roo.bootstrap.TableBody
10917  * @extends Roo.bootstrap.Component
10918  * @children Roo.bootstrap.TableRow
10919  * @parent Roo.bootstrap.Table
10920  * Bootstrap TableBody class
10921  * @cfg {String} cls element class
10922  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10923  * @cfg {String} align Aligns the content inside the element
10924  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10925  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10926  * 
10927  * @constructor
10928  * Create a new TableBody
10929  * @param {Object} config The config object
10930  */
10931
10932 Roo.bootstrap.TableBody = function(config){
10933     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10934 };
10935
10936 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10937     
10938     cls: false,
10939     tag: false,
10940     align: false,
10941     charoff: false,
10942     valign: false,
10943     
10944     getAutoCreate : function(){
10945         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10946         
10947         cfg = {
10948             tag: 'tbody'
10949         };
10950             
10951         if (this.cls) {
10952             cfg.cls=this.cls
10953         }
10954         if(this.tag){
10955             cfg.tag = this.tag;
10956         }
10957         
10958         if(this.align){
10959             cfg.align = this.align;
10960         }
10961         if(this.charoff){
10962             cfg.charoff = this.charoff;
10963         }
10964         if(this.valign){
10965             cfg.valign = this.valign;
10966         }
10967         
10968         return cfg;
10969     }
10970     
10971     
10972 //    initEvents : function()
10973 //    {
10974 //        
10975 //        if(!this.store){
10976 //            return;
10977 //        }
10978 //        
10979 //        this.store = Roo.factory(this.store, Roo.data);
10980 //        this.store.on('load', this.onLoad, this);
10981 //        
10982 //        this.store.load();
10983 //        
10984 //    },
10985 //    
10986 //    onLoad: function () 
10987 //    {   
10988 //        this.fireEvent('load', this);
10989 //    }
10990 //    
10991 //   
10992 });
10993
10994  
10995
10996  /*
10997  * Based on:
10998  * Ext JS Library 1.1.1
10999  * Copyright(c) 2006-2007, Ext JS, LLC.
11000  *
11001  * Originally Released Under LGPL - original licence link has changed is not relivant.
11002  *
11003  * Fork - LGPL
11004  * <script type="text/javascript">
11005  */
11006
11007 // as we use this in bootstrap.
11008 Roo.namespace('Roo.form');
11009  /**
11010  * @class Roo.form.Action
11011  * Internal Class used to handle form actions
11012  * @constructor
11013  * @param {Roo.form.BasicForm} el The form element or its id
11014  * @param {Object} config Configuration options
11015  */
11016
11017  
11018  
11019 // define the action interface
11020 Roo.form.Action = function(form, options){
11021     this.form = form;
11022     this.options = options || {};
11023 };
11024 /**
11025  * Client Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.CLIENT_INVALID = 'client';
11029 /**
11030  * Server Validation Failed
11031  * @const 
11032  */
11033 Roo.form.Action.SERVER_INVALID = 'server';
11034  /**
11035  * Connect to Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.CONNECT_FAILURE = 'connect';
11039 /**
11040  * Reading Data from Server Failed
11041  * @const 
11042  */
11043 Roo.form.Action.LOAD_FAILURE = 'load';
11044
11045 Roo.form.Action.prototype = {
11046     type : 'default',
11047     failureType : undefined,
11048     response : undefined,
11049     result : undefined,
11050
11051     // interface method
11052     run : function(options){
11053
11054     },
11055
11056     // interface method
11057     success : function(response){
11058
11059     },
11060
11061     // interface method
11062     handleResponse : function(response){
11063
11064     },
11065
11066     // default connection failure
11067     failure : function(response){
11068         
11069         this.response = response;
11070         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11071         this.form.afterAction(this, false);
11072     },
11073
11074     processResponse : function(response){
11075         this.response = response;
11076         if(!response.responseText){
11077             return true;
11078         }
11079         this.result = this.handleResponse(response);
11080         return this.result;
11081     },
11082
11083     // utility functions used internally
11084     getUrl : function(appendParams){
11085         var url = this.options.url || this.form.url || this.form.el.dom.action;
11086         if(appendParams){
11087             var p = this.getParams();
11088             if(p){
11089                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090             }
11091         }
11092         return url;
11093     },
11094
11095     getMethod : function(){
11096         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11097     },
11098
11099     getParams : function(){
11100         var bp = this.form.baseParams;
11101         var p = this.options.params;
11102         if(p){
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11105             }else if(typeof p == 'string' && bp){
11106                 p += '&' + Roo.urlEncode(bp);
11107             }
11108         }else if(bp){
11109             p = Roo.urlEncode(bp);
11110         }
11111         return p;
11112     },
11113
11114     createCallback : function(){
11115         return {
11116             success: this.success,
11117             failure: this.failure,
11118             scope: this,
11119             timeout: (this.form.timeout*1000),
11120             upload: this.form.fileUpload ? this.success : undefined
11121         };
11122     }
11123 };
11124
11125 Roo.form.Action.Submit = function(form, options){
11126     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11127 };
11128
11129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11130     type : 'submit',
11131
11132     haveProgress : false,
11133     uploadComplete : false,
11134     
11135     // uploadProgress indicator.
11136     uploadProgress : function()
11137     {
11138         if (!this.form.progressUrl) {
11139             return;
11140         }
11141         
11142         if (!this.haveProgress) {
11143             Roo.MessageBox.progress("Uploading", "Uploading");
11144         }
11145         if (this.uploadComplete) {
11146            Roo.MessageBox.hide();
11147            return;
11148         }
11149         
11150         this.haveProgress = true;
11151    
11152         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11153         
11154         var c = new Roo.data.Connection();
11155         c.request({
11156             url : this.form.progressUrl,
11157             params: {
11158                 id : uid
11159             },
11160             method: 'GET',
11161             success : function(req){
11162                //console.log(data);
11163                 var rdata = false;
11164                 var edata;
11165                 try  {
11166                    rdata = Roo.decode(req.responseText)
11167                 } catch (e) {
11168                     Roo.log("Invalid data from server..");
11169                     Roo.log(edata);
11170                     return;
11171                 }
11172                 if (!rdata || !rdata.success) {
11173                     Roo.log(rdata);
11174                     Roo.MessageBox.alert(Roo.encode(rdata));
11175                     return;
11176                 }
11177                 var data = rdata.data;
11178                 
11179                 if (this.uploadComplete) {
11180                    Roo.MessageBox.hide();
11181                    return;
11182                 }
11183                    
11184                 if (data){
11185                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11186                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11187                     );
11188                 }
11189                 this.uploadProgress.defer(2000,this);
11190             },
11191        
11192             failure: function(data) {
11193                 Roo.log('progress url failed ');
11194                 Roo.log(data);
11195             },
11196             scope : this
11197         });
11198            
11199     },
11200     
11201     
11202     run : function()
11203     {
11204         // run get Values on the form, so it syncs any secondary forms.
11205         this.form.getValues();
11206         
11207         var o = this.options;
11208         var method = this.getMethod();
11209         var isPost = method == 'POST';
11210         if(o.clientValidation === false || this.form.isValid()){
11211             
11212             if (this.form.progressUrl) {
11213                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11214                     (new Date() * 1) + '' + Math.random());
11215                     
11216             } 
11217             
11218             
11219             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11220                 form:this.form.el.dom,
11221                 url:this.getUrl(!isPost),
11222                 method: method,
11223                 params:isPost ? this.getParams() : null,
11224                 isUpload: this.form.fileUpload,
11225                 formData : this.form.formData
11226             }));
11227             
11228             this.uploadProgress();
11229
11230         }else if (o.clientValidation !== false){ // client validation failed
11231             this.failureType = Roo.form.Action.CLIENT_INVALID;
11232             this.form.afterAction(this, false);
11233         }
11234     },
11235
11236     success : function(response)
11237     {
11238         this.uploadComplete= true;
11239         if (this.haveProgress) {
11240             Roo.MessageBox.hide();
11241         }
11242         
11243         
11244         var result = this.processResponse(response);
11245         if(result === true || result.success){
11246             this.form.afterAction(this, true);
11247             return;
11248         }
11249         if(result.errors){
11250             this.form.markInvalid(result.errors);
11251             this.failureType = Roo.form.Action.SERVER_INVALID;
11252         }
11253         this.form.afterAction(this, false);
11254     },
11255     failure : function(response)
11256     {
11257         this.uploadComplete= true;
11258         if (this.haveProgress) {
11259             Roo.MessageBox.hide();
11260         }
11261         
11262         this.response = response;
11263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11264         this.form.afterAction(this, false);
11265     },
11266     
11267     handleResponse : function(response){
11268         if(this.form.errorReader){
11269             var rs = this.form.errorReader.read(response);
11270             var errors = [];
11271             if(rs.records){
11272                 for(var i = 0, len = rs.records.length; i < len; i++) {
11273                     var r = rs.records[i];
11274                     errors[i] = r.data;
11275                 }
11276             }
11277             if(errors.length < 1){
11278                 errors = null;
11279             }
11280             return {
11281                 success : rs.success,
11282                 errors : errors
11283             };
11284         }
11285         var ret = false;
11286         try {
11287             ret = Roo.decode(response.responseText);
11288         } catch (e) {
11289             ret = {
11290                 success: false,
11291                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11292                 errors : []
11293             };
11294         }
11295         return ret;
11296         
11297     }
11298 });
11299
11300
11301 Roo.form.Action.Load = function(form, options){
11302     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11303     this.reader = this.form.reader;
11304 };
11305
11306 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11307     type : 'load',
11308
11309     run : function(){
11310         
11311         Roo.Ajax.request(Roo.apply(
11312                 this.createCallback(), {
11313                     method:this.getMethod(),
11314                     url:this.getUrl(false),
11315                     params:this.getParams()
11316         }));
11317     },
11318
11319     success : function(response){
11320         
11321         var result = this.processResponse(response);
11322         if(result === true || !result.success || !result.data){
11323             this.failureType = Roo.form.Action.LOAD_FAILURE;
11324             this.form.afterAction(this, false);
11325             return;
11326         }
11327         this.form.clearInvalid();
11328         this.form.setValues(result.data);
11329         this.form.afterAction(this, true);
11330     },
11331
11332     handleResponse : function(response){
11333         if(this.form.reader){
11334             var rs = this.form.reader.read(response);
11335             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11336             return {
11337                 success : rs.success,
11338                 data : data
11339             };
11340         }
11341         return Roo.decode(response.responseText);
11342     }
11343 });
11344
11345 Roo.form.Action.ACTION_TYPES = {
11346     'load' : Roo.form.Action.Load,
11347     'submit' : Roo.form.Action.Submit
11348 };/*
11349  * - LGPL
11350  *
11351  * form
11352  *
11353  */
11354
11355 /**
11356  * @class Roo.bootstrap.form.Form
11357  * @extends Roo.bootstrap.Component
11358  * @children Roo.bootstrap.Component
11359  * Bootstrap Form class
11360  * @cfg {String} method  GET | POST (default POST)
11361  * @cfg {String} labelAlign top | left (default top)
11362  * @cfg {String} align left  | right - for navbars
11363  * @cfg {Boolean} loadMask load mask when submit (default true)
11364
11365  *
11366  * @constructor
11367  * Create a new Form
11368  * @param {Object} config The config object
11369  */
11370
11371
11372 Roo.bootstrap.form.Form = function(config){
11373     
11374     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11375     
11376     Roo.bootstrap.form.Form.popover.apply();
11377     
11378     this.addEvents({
11379         /**
11380          * @event clientvalidation
11381          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11382          * @param {Form} this
11383          * @param {Boolean} valid true if the form has passed client-side validation
11384          */
11385         clientvalidation: true,
11386         /**
11387          * @event beforeaction
11388          * Fires before any action is performed. Return false to cancel the action.
11389          * @param {Form} this
11390          * @param {Action} action The action to be performed
11391          */
11392         beforeaction: true,
11393         /**
11394          * @event actionfailed
11395          * Fires when an action fails.
11396          * @param {Form} this
11397          * @param {Action} action The action that failed
11398          */
11399         actionfailed : true,
11400         /**
11401          * @event actioncomplete
11402          * Fires when an action is completed.
11403          * @param {Form} this
11404          * @param {Action} action The action that completed
11405          */
11406         actioncomplete : true
11407     });
11408 };
11409
11410 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11411
11412      /**
11413      * @cfg {String} method
11414      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11415      */
11416     method : 'POST',
11417     /**
11418      * @cfg {String} url
11419      * The URL to use for form actions if one isn't supplied in the action options.
11420      */
11421     /**
11422      * @cfg {Boolean} fileUpload
11423      * Set to true if this form is a file upload.
11424      */
11425
11426     /**
11427      * @cfg {Object} baseParams
11428      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11429      */
11430
11431     /**
11432      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11433      */
11434     timeout: 30,
11435     /**
11436      * @cfg {Sting} align (left|right) for navbar forms
11437      */
11438     align : 'left',
11439
11440     // private
11441     activeAction : null,
11442
11443     /**
11444      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11445      * element by passing it or its id or mask the form itself by passing in true.
11446      * @type Mixed
11447      */
11448     waitMsgTarget : false,
11449
11450     loadMask : true,
11451     
11452     /**
11453      * @cfg {Boolean} errorMask (true|false) default false
11454      */
11455     errorMask : false,
11456     
11457     /**
11458      * @cfg {Number} maskOffset Default 100
11459      */
11460     maskOffset : 100,
11461     
11462     /**
11463      * @cfg {Boolean} maskBody
11464      */
11465     maskBody : false,
11466
11467     getAutoCreate : function(){
11468
11469         var cfg = {
11470             tag: 'form',
11471             method : this.method || 'POST',
11472             id : this.id || Roo.id(),
11473             cls : ''
11474         };
11475         if (this.parent().xtype.match(/^Nav/)) {
11476             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11477
11478         }
11479
11480         if (this.labelAlign == 'left' ) {
11481             cfg.cls += ' form-horizontal';
11482         }
11483
11484
11485         return cfg;
11486     },
11487     initEvents : function()
11488     {
11489         this.el.on('submit', this.onSubmit, this);
11490         // this was added as random key presses on the form where triggering form submit.
11491         this.el.on('keypress', function(e) {
11492             if (e.getCharCode() != 13) {
11493                 return true;
11494             }
11495             // we might need to allow it for textareas.. and some other items.
11496             // check e.getTarget().
11497
11498             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11499                 return true;
11500             }
11501
11502             Roo.log("keypress blocked");
11503
11504             e.preventDefault();
11505             return false;
11506         });
11507         
11508     },
11509     // private
11510     onSubmit : function(e){
11511         e.stopEvent();
11512     },
11513
11514      /**
11515      * Returns true if client-side validation on the form is successful.
11516      * @return Boolean
11517      */
11518     isValid : function(){
11519         var items = this.getItems();
11520         var valid = true;
11521         var target = false;
11522         
11523         items.each(function(f){
11524             
11525             if(f.validate()){
11526                 return;
11527             }
11528             
11529             Roo.log('invalid field: ' + f.name);
11530             
11531             valid = false;
11532
11533             if(!target && f.el.isVisible(true)){
11534                 target = f;
11535             }
11536            
11537         });
11538         
11539         if(this.errorMask && !valid){
11540             Roo.bootstrap.form.Form.popover.mask(this, target);
11541         }
11542         
11543         return valid;
11544     },
11545     
11546     /**
11547      * Returns true if any fields in this form have changed since their original load.
11548      * @return Boolean
11549      */
11550     isDirty : function(){
11551         var dirty = false;
11552         var items = this.getItems();
11553         items.each(function(f){
11554            if(f.isDirty()){
11555                dirty = true;
11556                return false;
11557            }
11558            return true;
11559         });
11560         return dirty;
11561     },
11562      /**
11563      * Performs a predefined action (submit or load) or custom actions you define on this form.
11564      * @param {String} actionName The name of the action type
11565      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11566      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11567      * accept other config options):
11568      * <pre>
11569 Property          Type             Description
11570 ----------------  ---------------  ----------------------------------------------------------------------------------
11571 url               String           The url for the action (defaults to the form's url)
11572 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11573 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11574 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11575                                    validate the form on the client (defaults to false)
11576      * </pre>
11577      * @return {BasicForm} this
11578      */
11579     doAction : function(action, options){
11580         if(typeof action == 'string'){
11581             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11582         }
11583         if(this.fireEvent('beforeaction', this, action) !== false){
11584             this.beforeAction(action);
11585             action.run.defer(100, action);
11586         }
11587         return this;
11588     },
11589
11590     // private
11591     beforeAction : function(action){
11592         var o = action.options;
11593         
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11598             } else {
11599                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600             }
11601         }
11602         // not really supported yet.. ??
11603
11604         //if(this.waitMsgTarget === true){
11605         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11606         //}else if(this.waitMsgTarget){
11607         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11608         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11609         //}else {
11610         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611        // }
11612
11613     },
11614
11615     // private
11616     afterAction : function(action, success){
11617         this.activeAction = null;
11618         var o = action.options;
11619
11620         if(this.loadMask){
11621             
11622             if(this.maskBody){
11623                 Roo.get(document.body).unmask();
11624             } else {
11625                 this.el.unmask();
11626             }
11627         }
11628         
11629         //if(this.waitMsgTarget === true){
11630 //            this.el.unmask();
11631         //}else if(this.waitMsgTarget){
11632         //    this.waitMsgTarget.unmask();
11633         //}else{
11634         //    Roo.MessageBox.updateProgress(1);
11635         //    Roo.MessageBox.hide();
11636        // }
11637         //
11638         if(success){
11639             if(o.reset){
11640                 this.reset();
11641             }
11642             Roo.callback(o.success, o.scope, [this, action]);
11643             this.fireEvent('actioncomplete', this, action);
11644
11645         }else{
11646
11647             // failure condition..
11648             // we have a scenario where updates need confirming.
11649             // eg. if a locking scenario exists..
11650             // we look for { errors : { needs_confirm : true }} in the response.
11651             if (
11652                 (typeof(action.result) != 'undefined')  &&
11653                 (typeof(action.result.errors) != 'undefined')  &&
11654                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11655            ){
11656                 var _t = this;
11657                 Roo.log("not supported yet");
11658                  /*
11659
11660                 Roo.MessageBox.confirm(
11661                     "Change requires confirmation",
11662                     action.result.errorMsg,
11663                     function(r) {
11664                         if (r != 'yes') {
11665                             return;
11666                         }
11667                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11668                     }
11669
11670                 );
11671                 */
11672
11673
11674                 return;
11675             }
11676
11677             Roo.callback(o.failure, o.scope, [this, action]);
11678             // show an error message if no failed handler is set..
11679             if (!this.hasListener('actionfailed')) {
11680                 Roo.log("need to add dialog support");
11681                 /*
11682                 Roo.MessageBox.alert("Error",
11683                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11684                         action.result.errorMsg :
11685                         "Saving Failed, please check your entries or try again"
11686                 );
11687                 */
11688             }
11689
11690             this.fireEvent('actionfailed', this, action);
11691         }
11692
11693     },
11694     /**
11695      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11696      * @param {String} id The value to search for
11697      * @return Field
11698      */
11699     findField : function(id){
11700         var items = this.getItems();
11701         var field = items.get(id);
11702         if(!field){
11703              items.each(function(f){
11704                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11705                     field = f;
11706                     return false;
11707                 }
11708                 return true;
11709             });
11710         }
11711         return field || null;
11712     },
11713      /**
11714      * Mark fields in this form invalid in bulk.
11715      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11716      * @return {BasicForm} this
11717      */
11718     markInvalid : function(errors){
11719         if(errors instanceof Array){
11720             for(var i = 0, len = errors.length; i < len; i++){
11721                 var fieldError = errors[i];
11722                 var f = this.findField(fieldError.id);
11723                 if(f){
11724                     f.markInvalid(fieldError.msg);
11725                 }
11726             }
11727         }else{
11728             var field, id;
11729             for(id in errors){
11730                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11731                     field.markInvalid(errors[id]);
11732                 }
11733             }
11734         }
11735         //Roo.each(this.childForms || [], function (f) {
11736         //    f.markInvalid(errors);
11737         //});
11738
11739         return this;
11740     },
11741
11742     /**
11743      * Set values for fields in this form in bulk.
11744      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11745      * @return {BasicForm} this
11746      */
11747     setValues : function(values){
11748         if(values instanceof Array){ // array of objects
11749             for(var i = 0, len = values.length; i < len; i++){
11750                 var v = values[i];
11751                 var f = this.findField(v.id);
11752                 if(f){
11753                     f.setValue(v.value);
11754                     if(this.trackResetOnLoad){
11755                         f.originalValue = f.getValue();
11756                     }
11757                 }
11758             }
11759         }else{ // object hash
11760             var field, id;
11761             for(id in values){
11762                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11763
11764                     if (field.setFromData &&
11765                         field.valueField &&
11766                         field.displayField &&
11767                         // combos' with local stores can
11768                         // be queried via setValue()
11769                         // to set their value..
11770                         (field.store && !field.store.isLocal)
11771                         ) {
11772                         // it's a combo
11773                         var sd = { };
11774                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11775                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11776                         field.setFromData(sd);
11777
11778                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11779                         
11780                         field.setFromData(values);
11781                         
11782                     } else {
11783                         field.setValue(values[id]);
11784                     }
11785
11786
11787                     if(this.trackResetOnLoad){
11788                         field.originalValue = field.getValue();
11789                     }
11790                 }
11791             }
11792         }
11793
11794         //Roo.each(this.childForms || [], function (f) {
11795         //    f.setValues(values);
11796         //});
11797
11798         return this;
11799     },
11800
11801     /**
11802      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11803      * they are returned as an array.
11804      * @param {Boolean} asString
11805      * @return {Object}
11806      */
11807     getValues : function(asString){
11808         //if (this.childForms) {
11809             // copy values from the child forms
11810         //    Roo.each(this.childForms, function (f) {
11811         //        this.setValues(f.getValues());
11812         //    }, this);
11813         //}
11814
11815
11816
11817         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11818         if(asString === true){
11819             return fs;
11820         }
11821         return Roo.urlDecode(fs);
11822     },
11823
11824     /**
11825      * Returns the fields in this form as an object with key/value pairs.
11826      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11827      * @return {Object}
11828      */
11829     getFieldValues : function(with_hidden)
11830     {
11831         var items = this.getItems();
11832         var ret = {};
11833         items.each(function(f){
11834             
11835             if (!f.getName()) {
11836                 return;
11837             }
11838             
11839             var v = f.getValue();
11840             
11841             if (f.inputType =='radio') {
11842                 if (typeof(ret[f.getName()]) == 'undefined') {
11843                     ret[f.getName()] = ''; // empty..
11844                 }
11845
11846                 if (!f.el.dom.checked) {
11847                     return;
11848
11849                 }
11850                 v = f.el.dom.value;
11851
11852             }
11853             
11854             if(f.xtype == 'MoneyField'){
11855                 ret[f.currencyName] = f.getCurrency();
11856             }
11857
11858             // not sure if this supported any more..
11859             if ((typeof(v) == 'object') && f.getRawValue) {
11860                 v = f.getRawValue() ; // dates..
11861             }
11862             // combo boxes where name != hiddenName...
11863             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11864                 ret[f.name] = f.getRawValue();
11865             }
11866             ret[f.getName()] = v;
11867         });
11868
11869         return ret;
11870     },
11871
11872     /**
11873      * Clears all invalid messages in this form.
11874      * @return {BasicForm} this
11875      */
11876     clearInvalid : function(){
11877         var items = this.getItems();
11878
11879         items.each(function(f){
11880            f.clearInvalid();
11881         });
11882
11883         return this;
11884     },
11885
11886     /**
11887      * Resets this form.
11888      * @return {BasicForm} this
11889      */
11890     reset : function(){
11891         var items = this.getItems();
11892         items.each(function(f){
11893             f.reset();
11894         });
11895
11896         Roo.each(this.childForms || [], function (f) {
11897             f.reset();
11898         });
11899
11900
11901         return this;
11902     },
11903     
11904     getItems : function()
11905     {
11906         var r=new Roo.util.MixedCollection(false, function(o){
11907             return o.id || (o.id = Roo.id());
11908         });
11909         var iter = function(el) {
11910             if (el.inputEl) {
11911                 r.add(el);
11912             }
11913             if (!el.items) {
11914                 return;
11915             }
11916             Roo.each(el.items,function(e) {
11917                 iter(e);
11918             });
11919         };
11920
11921         iter(this);
11922         return r;
11923     },
11924     
11925     hideFields : function(items)
11926     {
11927         Roo.each(items, function(i){
11928             
11929             var f = this.findField(i);
11930             
11931             if(!f){
11932                 return;
11933             }
11934             
11935             f.hide();
11936             
11937         }, this);
11938     },
11939     
11940     showFields : function(items)
11941     {
11942         Roo.each(items, function(i){
11943             
11944             var f = this.findField(i);
11945             
11946             if(!f){
11947                 return;
11948             }
11949             
11950             f.show();
11951             
11952         }, this);
11953     }
11954
11955 });
11956
11957 Roo.apply(Roo.bootstrap.form.Form, {
11958     
11959     popover : {
11960         
11961         padding : 5,
11962         
11963         isApplied : false,
11964         
11965         isMasked : false,
11966         
11967         form : false,
11968         
11969         target : false,
11970         
11971         toolTip : false,
11972         
11973         intervalID : false,
11974         
11975         maskEl : false,
11976         
11977         apply : function()
11978         {
11979             if(this.isApplied){
11980                 return;
11981             }
11982             
11983             this.maskEl = {
11984                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11985                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11986                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11987                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11988             };
11989             
11990             this.maskEl.top.enableDisplayMode("block");
11991             this.maskEl.left.enableDisplayMode("block");
11992             this.maskEl.bottom.enableDisplayMode("block");
11993             this.maskEl.right.enableDisplayMode("block");
11994             
11995             this.toolTip = new Roo.bootstrap.Tooltip({
11996                 cls : 'roo-form-error-popover',
11997                 alignment : {
11998                     'left' : ['r-l', [-2,0], 'right'],
11999                     'right' : ['l-r', [2,0], 'left'],
12000                     'bottom' : ['tl-bl', [0,2], 'top'],
12001                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12002                 }
12003             });
12004             
12005             this.toolTip.render(Roo.get(document.body));
12006
12007             this.toolTip.el.enableDisplayMode("block");
12008             
12009             Roo.get(document.body).on('click', function(){
12010                 this.unmask();
12011             }, this);
12012             
12013             Roo.get(document.body).on('touchstart', function(){
12014                 this.unmask();
12015             }, this);
12016             
12017             this.isApplied = true
12018         },
12019         
12020         mask : function(form, target)
12021         {
12022             this.form = form;
12023             
12024             this.target = target;
12025             
12026             if(!this.form.errorMask || !target.el){
12027                 return;
12028             }
12029             
12030             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12031             
12032             Roo.log(scrollable);
12033             
12034             var ot = this.target.el.calcOffsetsTo(scrollable);
12035             
12036             var scrollTo = ot[1] - this.form.maskOffset;
12037             
12038             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12039             
12040             scrollable.scrollTo('top', scrollTo);
12041             
12042             var box = this.target.el.getBox();
12043             Roo.log(box);
12044             var zIndex = Roo.bootstrap.Modal.zIndex++;
12045
12046             
12047             this.maskEl.top.setStyle('position', 'absolute');
12048             this.maskEl.top.setStyle('z-index', zIndex);
12049             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12050             this.maskEl.top.setLeft(0);
12051             this.maskEl.top.setTop(0);
12052             this.maskEl.top.show();
12053             
12054             this.maskEl.left.setStyle('position', 'absolute');
12055             this.maskEl.left.setStyle('z-index', zIndex);
12056             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12057             this.maskEl.left.setLeft(0);
12058             this.maskEl.left.setTop(box.y - this.padding);
12059             this.maskEl.left.show();
12060
12061             this.maskEl.bottom.setStyle('position', 'absolute');
12062             this.maskEl.bottom.setStyle('z-index', zIndex);
12063             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12064             this.maskEl.bottom.setLeft(0);
12065             this.maskEl.bottom.setTop(box.bottom + this.padding);
12066             this.maskEl.bottom.show();
12067
12068             this.maskEl.right.setStyle('position', 'absolute');
12069             this.maskEl.right.setStyle('z-index', zIndex);
12070             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12071             this.maskEl.right.setLeft(box.right + this.padding);
12072             this.maskEl.right.setTop(box.y - this.padding);
12073             this.maskEl.right.show();
12074
12075             this.toolTip.bindEl = this.target.el;
12076
12077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12078
12079             var tip = this.target.blankText;
12080
12081             if(this.target.getValue() !== '' ) {
12082                 
12083                 if (this.target.invalidText.length) {
12084                     tip = this.target.invalidText;
12085                 } else if (this.target.regexText.length){
12086                     tip = this.target.regexText;
12087                 }
12088             }
12089
12090             this.toolTip.show(tip);
12091
12092             this.intervalID = window.setInterval(function() {
12093                 Roo.bootstrap.form.Form.popover.unmask();
12094             }, 10000);
12095
12096             window.onwheel = function(){ return false;};
12097             
12098             (function(){ this.isMasked = true; }).defer(500, this);
12099             
12100         },
12101         
12102         unmask : function()
12103         {
12104             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12105                 return;
12106             }
12107             
12108             this.maskEl.top.setStyle('position', 'absolute');
12109             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12110             this.maskEl.top.hide();
12111
12112             this.maskEl.left.setStyle('position', 'absolute');
12113             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12114             this.maskEl.left.hide();
12115
12116             this.maskEl.bottom.setStyle('position', 'absolute');
12117             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12118             this.maskEl.bottom.hide();
12119
12120             this.maskEl.right.setStyle('position', 'absolute');
12121             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12122             this.maskEl.right.hide();
12123             
12124             this.toolTip.hide();
12125             
12126             this.toolTip.el.hide();
12127             
12128             window.onwheel = function(){ return true;};
12129             
12130             if(this.intervalID){
12131                 window.clearInterval(this.intervalID);
12132                 this.intervalID = false;
12133             }
12134             
12135             this.isMasked = false;
12136             
12137         }
12138         
12139     }
12140     
12141 });
12142
12143 /*
12144  * Based on:
12145  * Ext JS Library 1.1.1
12146  * Copyright(c) 2006-2007, Ext JS, LLC.
12147  *
12148  * Originally Released Under LGPL - original licence link has changed is not relivant.
12149  *
12150  * Fork - LGPL
12151  * <script type="text/javascript">
12152  */
12153 /**
12154  * @class Roo.form.VTypes
12155  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12156  * @static
12157  */
12158 Roo.form.VTypes = function(){
12159     // closure these in so they are only created once.
12160     var alpha = /^[a-zA-Z_]+$/;
12161     var alphanum = /^[a-zA-Z0-9_]+$/;
12162     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12163     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12164
12165     // All these messages and functions are configurable
12166     return {
12167         /**
12168          * The function used to validate email addresses
12169          * @param {String} value The email address
12170          */
12171         'email' : function(v){
12172             return email.test(v);
12173         },
12174         /**
12175          * The error text to display when the email validation function returns false
12176          * @type String
12177          */
12178         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12179         /**
12180          * The keystroke filter mask to be applied on email input
12181          * @type RegExp
12182          */
12183         'emailMask' : /[a-z0-9_\.\-@]/i,
12184
12185         /**
12186          * The function used to validate URLs
12187          * @param {String} value The URL
12188          */
12189         'url' : function(v){
12190             return url.test(v);
12191         },
12192         /**
12193          * The error text to display when the url validation function returns false
12194          * @type String
12195          */
12196         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12197         
12198         /**
12199          * The function used to validate alpha values
12200          * @param {String} value The value
12201          */
12202         'alpha' : function(v){
12203             return alpha.test(v);
12204         },
12205         /**
12206          * The error text to display when the alpha validation function returns false
12207          * @type String
12208          */
12209         'alphaText' : 'This field should only contain letters and _',
12210         /**
12211          * The keystroke filter mask to be applied on alpha input
12212          * @type RegExp
12213          */
12214         'alphaMask' : /[a-z_]/i,
12215
12216         /**
12217          * The function used to validate alphanumeric values
12218          * @param {String} value The value
12219          */
12220         'alphanum' : function(v){
12221             return alphanum.test(v);
12222         },
12223         /**
12224          * The error text to display when the alphanumeric validation function returns false
12225          * @type String
12226          */
12227         'alphanumText' : 'This field should only contain letters, numbers and _',
12228         /**
12229          * The keystroke filter mask to be applied on alphanumeric input
12230          * @type RegExp
12231          */
12232         'alphanumMask' : /[a-z0-9_]/i
12233     };
12234 }();/*
12235  * - LGPL
12236  *
12237  * Input
12238  * 
12239  */
12240
12241 /**
12242  * @class Roo.bootstrap.form.Input
12243  * @extends Roo.bootstrap.Component
12244  * Bootstrap Input class
12245  * @cfg {Boolean} disabled is it disabled
12246  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12247  * @cfg {String} name name of the input
12248  * @cfg {string} fieldLabel - the label associated
12249  * @cfg {string} placeholder - placeholder to put in text.
12250  * @cfg {string} before - input group add on before
12251  * @cfg {string} after - input group add on after
12252  * @cfg {string} size - (lg|sm) or leave empty..
12253  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12254  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12255  * @cfg {Number} md colspan out of 12 for computer-sized screens
12256  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12257  * @cfg {string} value default value of the input
12258  * @cfg {Number} labelWidth set the width of label 
12259  * @cfg {Number} labellg set the width of label (1-12)
12260  * @cfg {Number} labelmd set the width of label (1-12)
12261  * @cfg {Number} labelsm set the width of label (1-12)
12262  * @cfg {Number} labelxs set the width of label (1-12)
12263  * @cfg {String} labelAlign (top|left)
12264  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12265  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12266  * @cfg {String} indicatorpos (left|right) default left
12267  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12268  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12269  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12270  * @cfg {Roo.bootstrap.Button} before Button to show before
12271  * @cfg {Roo.bootstrap.Button} afterButton to show before
12272  * @cfg {String} align (left|center|right) Default left
12273  * @cfg {Boolean} forceFeedback (true|false) Default false
12274  * 
12275  * @constructor
12276  * Create a new Input
12277  * @param {Object} config The config object
12278  */
12279
12280 Roo.bootstrap.form.Input = function(config){
12281     
12282     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12283     
12284     this.addEvents({
12285         /**
12286          * @event focus
12287          * Fires when this field receives input focus.
12288          * @param {Roo.form.Field} this
12289          */
12290         focus : true,
12291         /**
12292          * @event blur
12293          * Fires when this field loses input focus.
12294          * @param {Roo.form.Field} this
12295          */
12296         blur : true,
12297         /**
12298          * @event specialkey
12299          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12300          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12301          * @param {Roo.form.Field} this
12302          * @param {Roo.EventObject} e The event object
12303          */
12304         specialkey : true,
12305         /**
12306          * @event change
12307          * Fires just before the field blurs if the field value has changed.
12308          * @param {Roo.form.Field} this
12309          * @param {Mixed} newValue The new value
12310          * @param {Mixed} oldValue The original value
12311          */
12312         change : true,
12313         /**
12314          * @event invalid
12315          * Fires after the field has been marked as invalid.
12316          * @param {Roo.form.Field} this
12317          * @param {String} msg The validation message
12318          */
12319         invalid : true,
12320         /**
12321          * @event valid
12322          * Fires after the field has been validated with no errors.
12323          * @param {Roo.form.Field} this
12324          */
12325         valid : true,
12326          /**
12327          * @event keyup
12328          * Fires after the key up
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject}  e The event Object
12331          */
12332         keyup : true,
12333         /**
12334          * @event paste
12335          * Fires after the user pastes into input
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject}  e The event Object
12338          */
12339         paste : true
12340     });
12341 };
12342
12343 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12344      /**
12345      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12346       automatic validation (defaults to "keyup").
12347      */
12348     validationEvent : "keyup",
12349      /**
12350      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12351      */
12352     validateOnBlur : true,
12353     /**
12354      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12355      */
12356     validationDelay : 250,
12357      /**
12358      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12359      */
12360     focusClass : "x-form-focus",  // not needed???
12361     
12362        
12363     /**
12364      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     invalidClass : "has-warning",
12367     
12368     /**
12369      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12370      */
12371     validClass : "has-success",
12372     
12373     /**
12374      * @cfg {Boolean} hasFeedback (true|false) default true
12375      */
12376     hasFeedback : true,
12377     
12378     /**
12379      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     invalidFeedbackClass : "glyphicon-warning-sign",
12382     
12383     /**
12384      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12385      */
12386     validFeedbackClass : "glyphicon-ok",
12387     
12388     /**
12389      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12390      */
12391     selectOnFocus : false,
12392     
12393      /**
12394      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12395      */
12396     maskRe : null,
12397        /**
12398      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12399      */
12400     vtype : null,
12401     
12402       /**
12403      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12404      */
12405     disableKeyFilter : false,
12406     
12407        /**
12408      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12409      */
12410     disabled : false,
12411      /**
12412      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12413      */
12414     allowBlank : true,
12415     /**
12416      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12417      */
12418     blankText : "Please complete this mandatory field",
12419     
12420      /**
12421      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12422      */
12423     minLength : 0,
12424     /**
12425      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12426      */
12427     maxLength : Number.MAX_VALUE,
12428     /**
12429      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12430      */
12431     minLengthText : "The minimum length for this field is {0}",
12432     /**
12433      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12434      */
12435     maxLengthText : "The maximum length for this field is {0}",
12436   
12437     
12438     /**
12439      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12440      * If available, this function will be called only after the basic validators all return true, and will be passed the
12441      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12442      */
12443     validator : null,
12444     /**
12445      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12446      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12447      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12448      */
12449     regex : null,
12450     /**
12451      * @cfg {String} regexText -- Depricated - use Invalid Text
12452      */
12453     regexText : "",
12454     
12455     /**
12456      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457      */
12458     invalidText : "",
12459     
12460     
12461     
12462     autocomplete: false,
12463     
12464     
12465     fieldLabel : '',
12466     inputType : 'text',
12467     
12468     name : false,
12469     placeholder: false,
12470     before : false,
12471     after : false,
12472     size : false,
12473     hasFocus : false,
12474     preventMark: false,
12475     isFormField : true,
12476     value : '',
12477     labelWidth : 2,
12478     labelAlign : false,
12479     readOnly : false,
12480     align : false,
12481     formatedValue : false,
12482     forceFeedback : false,
12483     
12484     indicatorpos : 'left',
12485     
12486     labellg : 0,
12487     labelmd : 0,
12488     labelsm : 0,
12489     labelxs : 0,
12490     
12491     capture : '',
12492     accept : '',
12493     
12494     parentLabelAlign : function()
12495     {
12496         var parent = this;
12497         while (parent.parent()) {
12498             parent = parent.parent();
12499             if (typeof(parent.labelAlign) !='undefined') {
12500                 return parent.labelAlign;
12501             }
12502         }
12503         return 'left';
12504         
12505     },
12506     
12507     getAutoCreate : function()
12508     {
12509         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510         
12511         var id = Roo.id();
12512         
12513         var cfg = {};
12514         
12515         if(this.inputType != 'hidden'){
12516             cfg.cls = 'form-group' //input-group
12517         }
12518         
12519         var input =  {
12520             tag: 'input',
12521             id : id,
12522             type : this.inputType,
12523             value : this.value,
12524             cls : 'form-control',
12525             placeholder : this.placeholder || '',
12526             autocomplete : this.autocomplete || 'new-password'
12527         };
12528         if (this.inputType == 'file') {
12529             input.style = 'overflow:hidden'; // why not in CSS?
12530         }
12531         
12532         if(this.capture.length){
12533             input.capture = this.capture;
12534         }
12535         
12536         if(this.accept.length){
12537             input.accept = this.accept + "/*";
12538         }
12539         
12540         if(this.align){
12541             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12542         }
12543         
12544         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12545             input.maxLength = this.maxLength;
12546         }
12547         
12548         if (this.disabled) {
12549             input.disabled=true;
12550         }
12551         
12552         if (this.readOnly) {
12553             input.readonly=true;
12554         }
12555         
12556         if (this.name) {
12557             input.name = this.name;
12558         }
12559         
12560         if (this.size) {
12561             input.cls += ' input-' + this.size;
12562         }
12563         
12564         var settings=this;
12565         ['xs','sm','md','lg'].map(function(size){
12566             if (settings[size]) {
12567                 cfg.cls += ' col-' + size + '-' + settings[size];
12568             }
12569         });
12570         
12571         var inputblock = input;
12572         
12573         var feedback = {
12574             tag: 'span',
12575             cls: 'glyphicon form-control-feedback'
12576         };
12577             
12578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12579             
12580             inputblock = {
12581                 cls : 'has-feedback',
12582                 cn :  [
12583                     input,
12584                     feedback
12585                 ] 
12586             };  
12587         }
12588         
12589         if (this.before || this.after) {
12590             
12591             inputblock = {
12592                 cls : 'input-group',
12593                 cn :  [] 
12594             };
12595             
12596             if (this.before && typeof(this.before) == 'string') {
12597                 
12598                 inputblock.cn.push({
12599                     tag :'span',
12600                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12601                     html : this.before
12602                 });
12603             }
12604             if (this.before && typeof(this.before) == 'object') {
12605                 this.before = Roo.factory(this.before);
12606                 
12607                 inputblock.cn.push({
12608                     tag :'span',
12609                     cls : 'roo-input-before input-group-prepend   input-group-' +
12610                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12611                 });
12612             }
12613             
12614             inputblock.cn.push(input);
12615             
12616             if (this.after && typeof(this.after) == 'string') {
12617                 inputblock.cn.push({
12618                     tag :'span',
12619                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12620                     html : this.after
12621                 });
12622             }
12623             if (this.after && typeof(this.after) == 'object') {
12624                 this.after = Roo.factory(this.after);
12625                 
12626                 inputblock.cn.push({
12627                     tag :'span',
12628                     cls : 'roo-input-after input-group-append  input-group-' +
12629                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12630                 });
12631             }
12632             
12633             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12634                 inputblock.cls += ' has-feedback';
12635                 inputblock.cn.push(feedback);
12636             }
12637         };
12638         var indicator = {
12639             tag : 'i',
12640             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12641             tooltip : 'This field is required'
12642         };
12643         if (this.allowBlank ) {
12644             indicator.style = this.allowBlank ? ' display:none' : '';
12645         }
12646         if (align ==='left' && this.fieldLabel.length) {
12647             
12648             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12649             
12650             cfg.cn = [
12651                 indicator,
12652                 {
12653                     tag: 'label',
12654                     'for' :  id,
12655                     cls : 'control-label col-form-label',
12656                     html : this.fieldLabel
12657
12658                 },
12659                 {
12660                     cls : "", 
12661                     cn: [
12662                         inputblock
12663                     ]
12664                 }
12665             ];
12666             
12667             var labelCfg = cfg.cn[1];
12668             var contentCfg = cfg.cn[2];
12669             
12670             if(this.indicatorpos == 'right'){
12671                 cfg.cn = [
12672                     {
12673                         tag: 'label',
12674                         'for' :  id,
12675                         cls : 'control-label col-form-label',
12676                         cn : [
12677                             {
12678                                 tag : 'span',
12679                                 html : this.fieldLabel
12680                             },
12681                             indicator
12682                         ]
12683                     },
12684                     {
12685                         cls : "",
12686                         cn: [
12687                             inputblock
12688                         ]
12689                     }
12690
12691                 ];
12692                 
12693                 labelCfg = cfg.cn[0];
12694                 contentCfg = cfg.cn[1];
12695             
12696             }
12697             
12698             if(this.labelWidth > 12){
12699                 labelCfg.style = "width: " + this.labelWidth + 'px';
12700             }
12701             
12702             if(this.labelWidth < 13 && this.labelmd == 0){
12703                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12704             }
12705             
12706             if(this.labellg > 0){
12707                 labelCfg.cls += ' col-lg-' + this.labellg;
12708                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12709             }
12710             
12711             if(this.labelmd > 0){
12712                 labelCfg.cls += ' col-md-' + this.labelmd;
12713                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12714             }
12715             
12716             if(this.labelsm > 0){
12717                 labelCfg.cls += ' col-sm-' + this.labelsm;
12718                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12719             }
12720             
12721             if(this.labelxs > 0){
12722                 labelCfg.cls += ' col-xs-' + this.labelxs;
12723                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12724             }
12725             
12726             
12727         } else if ( this.fieldLabel.length) {
12728                 
12729             
12730             
12731             cfg.cn = [
12732                 {
12733                     tag : 'i',
12734                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12735                     tooltip : 'This field is required',
12736                     style : this.allowBlank ? ' display:none' : '' 
12737                 },
12738                 {
12739                     tag: 'label',
12740                    //cls : 'input-group-addon',
12741                     html : this.fieldLabel
12742
12743                 },
12744
12745                inputblock
12746
12747            ];
12748            
12749            if(this.indicatorpos == 'right'){
12750        
12751                 cfg.cn = [
12752                     {
12753                         tag: 'label',
12754                        //cls : 'input-group-addon',
12755                         html : this.fieldLabel
12756
12757                     },
12758                     {
12759                         tag : 'i',
12760                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12761                         tooltip : 'This field is required',
12762                         style : this.allowBlank ? ' display:none' : '' 
12763                     },
12764
12765                    inputblock
12766
12767                ];
12768
12769             }
12770
12771         } else {
12772             
12773             cfg.cn = [
12774
12775                     inputblock
12776
12777             ];
12778                 
12779                 
12780         };
12781         
12782         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12783            cfg.cls += ' navbar-form';
12784         }
12785         
12786         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12787             // on BS4 we do this only if not form 
12788             cfg.cls += ' navbar-form';
12789             cfg.tag = 'li';
12790         }
12791         
12792         return cfg;
12793         
12794     },
12795     /**
12796      * return the real input element.
12797      */
12798     inputEl: function ()
12799     {
12800         return this.el.select('input.form-control',true).first();
12801     },
12802     
12803     tooltipEl : function()
12804     {
12805         return this.inputEl();
12806     },
12807     
12808     indicatorEl : function()
12809     {
12810         if (Roo.bootstrap.version == 4) {
12811             return false; // not enabled in v4 yet.
12812         }
12813         
12814         var indicator = this.el.select('i.roo-required-indicator',true).first();
12815         
12816         if(!indicator){
12817             return false;
12818         }
12819         
12820         return indicator;
12821         
12822     },
12823     
12824     setDisabled : function(v)
12825     {
12826         var i  = this.inputEl().dom;
12827         if (!v) {
12828             i.removeAttribute('disabled');
12829             return;
12830             
12831         }
12832         i.setAttribute('disabled','true');
12833     },
12834     initEvents : function()
12835     {
12836           
12837         this.inputEl().on("keydown" , this.fireKey,  this);
12838         this.inputEl().on("focus", this.onFocus,  this);
12839         this.inputEl().on("blur", this.onBlur,  this);
12840         
12841         this.inputEl().relayEvent('keyup', this);
12842         this.inputEl().relayEvent('paste', this);
12843         
12844         this.indicator = this.indicatorEl();
12845         
12846         if(this.indicator){
12847             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12848         }
12849  
12850         // reference to original value for reset
12851         this.originalValue = this.getValue();
12852         //Roo.form.TextField.superclass.initEvents.call(this);
12853         if(this.validationEvent == 'keyup'){
12854             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12855             this.inputEl().on('keyup', this.filterValidation, this);
12856         }
12857         else if(this.validationEvent !== false){
12858             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12859         }
12860         
12861         if(this.selectOnFocus){
12862             this.on("focus", this.preFocus, this);
12863             
12864         }
12865         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12866             this.inputEl().on("keypress", this.filterKeys, this);
12867         } else {
12868             this.inputEl().relayEvent('keypress', this);
12869         }
12870        /* if(this.grow){
12871             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12872             this.el.on("click", this.autoSize,  this);
12873         }
12874         */
12875         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12876             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12877         }
12878         
12879         if (typeof(this.before) == 'object') {
12880             this.before.render(this.el.select('.roo-input-before',true).first());
12881         }
12882         if (typeof(this.after) == 'object') {
12883             this.after.render(this.el.select('.roo-input-after',true).first());
12884         }
12885         
12886         this.inputEl().on('change', this.onChange, this);
12887         
12888     },
12889     filterValidation : function(e){
12890         if(!e.isNavKeyPress()){
12891             this.validationTask.delay(this.validationDelay);
12892         }
12893     },
12894      /**
12895      * Validates the field value
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validate : function(){
12899         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12900         if(this.disabled || this.validateValue(this.getRawValue())){
12901             this.markValid();
12902             return true;
12903         }
12904         
12905         this.markInvalid();
12906         return false;
12907     },
12908     
12909     
12910     /**
12911      * Validates a value according to the field's validation rules and marks the field as invalid
12912      * if the validation fails
12913      * @param {Mixed} value The value to validate
12914      * @return {Boolean} True if the value is valid, else false
12915      */
12916     validateValue : function(value)
12917     {
12918         if(this.getVisibilityEl().hasClass('hidden')){
12919             return true;
12920         }
12921         
12922         if(value.length < 1)  { // if it's blank
12923             if(this.allowBlank){
12924                 return true;
12925             }
12926             return false;
12927         }
12928         
12929         if(value.length < this.minLength){
12930             return false;
12931         }
12932         if(value.length > this.maxLength){
12933             return false;
12934         }
12935         if(this.vtype){
12936             var vt = Roo.form.VTypes;
12937             if(!vt[this.vtype](value, this)){
12938                 return false;
12939             }
12940         }
12941         if(typeof this.validator == "function"){
12942             var msg = this.validator(value);
12943             if (typeof(msg) == 'string') {
12944                 this.invalidText = msg;
12945             }
12946             if(msg !== true){
12947                 return false;
12948             }
12949         }
12950         
12951         if(this.regex && !this.regex.test(value)){
12952             return false;
12953         }
12954         
12955         return true;
12956     },
12957     
12958      // private
12959     fireKey : function(e){
12960         //Roo.log('field ' + e.getKey());
12961         if(e.isNavKeyPress()){
12962             this.fireEvent("specialkey", this, e);
12963         }
12964     },
12965     focus : function (selectText){
12966         if(this.rendered){
12967             this.inputEl().focus();
12968             if(selectText === true){
12969                 this.inputEl().dom.select();
12970             }
12971         }
12972         return this;
12973     } ,
12974     
12975     onFocus : function(){
12976         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12977            // this.el.addClass(this.focusClass);
12978         }
12979         if(!this.hasFocus){
12980             this.hasFocus = true;
12981             this.startValue = this.getValue();
12982             this.fireEvent("focus", this);
12983         }
12984     },
12985     
12986     beforeBlur : Roo.emptyFn,
12987
12988     
12989     // private
12990     onBlur : function(){
12991         this.beforeBlur();
12992         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12993             //this.el.removeClass(this.focusClass);
12994         }
12995         this.hasFocus = false;
12996         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12997             this.validate();
12998         }
12999         var v = this.getValue();
13000         if(String(v) !== String(this.startValue)){
13001             this.fireEvent('change', this, v, this.startValue);
13002         }
13003         this.fireEvent("blur", this);
13004     },
13005     
13006     onChange : function(e)
13007     {
13008         var v = this.getValue();
13009         if(String(v) !== String(this.startValue)){
13010             this.fireEvent('change', this, v, this.startValue);
13011         }
13012         
13013     },
13014     
13015     /**
13016      * Resets the current field value to the originally loaded value and clears any validation messages
13017      */
13018     reset : function(){
13019         this.setValue(this.originalValue);
13020         this.validate();
13021     },
13022      /**
13023      * Returns the name of the field
13024      * @return {Mixed} name The name field
13025      */
13026     getName: function(){
13027         return this.name;
13028     },
13029      /**
13030      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13031      * @return {Mixed} value The field value
13032      */
13033     getValue : function(){
13034         
13035         var v = this.inputEl().getValue();
13036         
13037         return v;
13038     },
13039     /**
13040      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13041      * @return {Mixed} value The field value
13042      */
13043     getRawValue : function(){
13044         var v = this.inputEl().getValue();
13045         
13046         return v;
13047     },
13048     
13049     /**
13050      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13051      * @param {Mixed} value The value to set
13052      */
13053     setRawValue : function(v){
13054         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13055     },
13056     
13057     selectText : function(start, end){
13058         var v = this.getRawValue();
13059         if(v.length > 0){
13060             start = start === undefined ? 0 : start;
13061             end = end === undefined ? v.length : end;
13062             var d = this.inputEl().dom;
13063             if(d.setSelectionRange){
13064                 d.setSelectionRange(start, end);
13065             }else if(d.createTextRange){
13066                 var range = d.createTextRange();
13067                 range.moveStart("character", start);
13068                 range.moveEnd("character", v.length-end);
13069                 range.select();
13070             }
13071         }
13072     },
13073     
13074     /**
13075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13076      * @param {Mixed} value The value to set
13077      */
13078     setValue : function(v){
13079         this.value = v;
13080         if(this.rendered){
13081             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082             this.validate();
13083         }
13084     },
13085     
13086     /*
13087     processValue : function(value){
13088         if(this.stripCharsRe){
13089             var newValue = value.replace(this.stripCharsRe, '');
13090             if(newValue !== value){
13091                 this.setRawValue(newValue);
13092                 return newValue;
13093             }
13094         }
13095         return value;
13096     },
13097   */
13098     preFocus : function(){
13099         
13100         if(this.selectOnFocus){
13101             this.inputEl().dom.select();
13102         }
13103     },
13104     filterKeys : function(e){
13105         var k = e.getKey();
13106         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13107             return;
13108         }
13109         var c = e.getCharCode(), cc = String.fromCharCode(c);
13110         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13111             return;
13112         }
13113         if(!this.maskRe.test(cc)){
13114             e.stopEvent();
13115         }
13116     },
13117      /**
13118      * Clear any invalid styles/messages for this field
13119      */
13120     clearInvalid : function(){
13121         
13122         if(!this.el || this.preventMark){ // not rendered
13123             return;
13124         }
13125         
13126         
13127         this.el.removeClass([this.invalidClass, 'is-invalid']);
13128         
13129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130             
13131             var feedback = this.el.select('.form-control-feedback', true).first();
13132             
13133             if(feedback){
13134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13135             }
13136             
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         this.fireEvent('valid', this);
13145     },
13146     
13147      /**
13148      * Mark this field as valid
13149      */
13150     markValid : function()
13151     {
13152         if(!this.el  || this.preventMark){ // not rendered...
13153             return;
13154         }
13155         
13156         this.el.removeClass([this.invalidClass, this.validClass]);
13157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158
13159         var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161         if(feedback){
13162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13163         }
13164         
13165         if(this.indicator){
13166             this.indicator.removeClass('visible');
13167             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13168         }
13169         
13170         if(this.disabled){
13171             return;
13172         }
13173         
13174            
13175         if(this.allowBlank && !this.getRawValue().length){
13176             return;
13177         }
13178         if (Roo.bootstrap.version == 3) {
13179             this.el.addClass(this.validClass);
13180         } else {
13181             this.inputEl().addClass('is-valid');
13182         }
13183
13184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13185             
13186             var feedback = this.el.select('.form-control-feedback', true).first();
13187             
13188             if(feedback){
13189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13191             }
13192             
13193         }
13194         
13195         this.fireEvent('valid', this);
13196     },
13197     
13198      /**
13199      * Mark this field as invalid
13200      * @param {String} msg The validation message
13201      */
13202     markInvalid : function(msg)
13203     {
13204         if(!this.el  || this.preventMark){ // not rendered
13205             return;
13206         }
13207         
13208         this.el.removeClass([this.invalidClass, this.validClass]);
13209         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13210         
13211         var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213         if(feedback){
13214             this.el.select('.form-control-feedback', true).first().removeClass(
13215                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13216         }
13217
13218         if(this.disabled){
13219             return;
13220         }
13221         
13222         if(this.allowBlank && !this.getRawValue().length){
13223             return;
13224         }
13225         
13226         if(this.indicator){
13227             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13228             this.indicator.addClass('visible');
13229         }
13230         if (Roo.bootstrap.version == 3) {
13231             this.el.addClass(this.invalidClass);
13232         } else {
13233             this.inputEl().addClass('is-invalid');
13234         }
13235         
13236         
13237         
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 
13245                 if(this.getValue().length || this.forceFeedback){
13246                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247                 }
13248                 
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('invalid', this, msg);
13254     },
13255     // private
13256     SafariOnKeyDown : function(event)
13257     {
13258         // this is a workaround for a password hang bug on chrome/ webkit.
13259         if (this.inputEl().dom.type != 'password') {
13260             return;
13261         }
13262         
13263         var isSelectAll = false;
13264         
13265         if(this.inputEl().dom.selectionEnd > 0){
13266             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13267         }
13268         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13269             event.preventDefault();
13270             this.setValue('');
13271             return;
13272         }
13273         
13274         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13275             
13276             event.preventDefault();
13277             // this is very hacky as keydown always get's upper case.
13278             //
13279             var cc = String.fromCharCode(event.getCharCode());
13280             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13281             
13282         }
13283     },
13284     adjustWidth : function(tag, w){
13285         tag = tag.toLowerCase();
13286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13288                 if(tag == 'input'){
13289                     return w + 2;
13290                 }
13291                 if(tag == 'textarea'){
13292                     return w-2;
13293                 }
13294             }else if(Roo.isOpera){
13295                 if(tag == 'input'){
13296                     return w + 2;
13297                 }
13298                 if(tag == 'textarea'){
13299                     return w-2;
13300                 }
13301             }
13302         }
13303         return w;
13304     },
13305     
13306     setFieldLabel : function(v)
13307     {
13308         if(!this.rendered){
13309             return;
13310         }
13311         
13312         if(this.indicatorEl()){
13313             var ar = this.el.select('label > span',true);
13314             
13315             if (ar.elements.length) {
13316                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317                 this.fieldLabel = v;
13318                 return;
13319             }
13320             
13321             var br = this.el.select('label',true);
13322             
13323             if(br.elements.length) {
13324                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13325                 this.fieldLabel = v;
13326                 return;
13327             }
13328             
13329             Roo.log('Cannot Found any of label > span || label in input');
13330             return;
13331         }
13332         
13333         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13334         this.fieldLabel = v;
13335         
13336         
13337     }
13338 });
13339
13340  
13341 /*
13342  * - LGPL
13343  *
13344  * Input
13345  * 
13346  */
13347
13348 /**
13349  * @class Roo.bootstrap.form.TextArea
13350  * @extends Roo.bootstrap.form.Input
13351  * Bootstrap TextArea class
13352  * @cfg {Number} cols Specifies the visible width of a text area
13353  * @cfg {Number} rows Specifies the visible number of lines in a text area
13354  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13355  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13356  * @cfg {string} html text
13357  * 
13358  * @constructor
13359  * Create a new TextArea
13360  * @param {Object} config The config object
13361  */
13362
13363 Roo.bootstrap.form.TextArea = function(config){
13364     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13365    
13366 };
13367
13368 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13369      
13370     cols : false,
13371     rows : 5,
13372     readOnly : false,
13373     warp : 'soft',
13374     resize : false,
13375     value: false,
13376     html: false,
13377     
13378     getAutoCreate : function(){
13379         
13380         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381         
13382         var id = Roo.id();
13383         
13384         var cfg = {};
13385         
13386         if(this.inputType != 'hidden'){
13387             cfg.cls = 'form-group' //input-group
13388         }
13389         
13390         var input =  {
13391             tag: 'textarea',
13392             id : id,
13393             warp : this.warp,
13394             rows : this.rows,
13395             value : this.value || '',
13396             html: this.html || '',
13397             cls : 'form-control',
13398             placeholder : this.placeholder || '' 
13399             
13400         };
13401         
13402         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13403             input.maxLength = this.maxLength;
13404         }
13405         
13406         if(this.resize){
13407             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13408         }
13409         
13410         if(this.cols){
13411             input.cols = this.cols;
13412         }
13413         
13414         if (this.readOnly) {
13415             input.readonly = true;
13416         }
13417         
13418         if (this.name) {
13419             input.name = this.name;
13420         }
13421         
13422         if (this.size) {
13423             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13424         }
13425         
13426         var settings=this;
13427         ['xs','sm','md','lg'].map(function(size){
13428             if (settings[size]) {
13429                 cfg.cls += ' col-' + size + '-' + settings[size];
13430             }
13431         });
13432         
13433         var inputblock = input;
13434         
13435         if(this.hasFeedback && !this.allowBlank){
13436             
13437             var feedback = {
13438                 tag: 'span',
13439                 cls: 'glyphicon form-control-feedback'
13440             };
13441
13442             inputblock = {
13443                 cls : 'has-feedback',
13444                 cn :  [
13445                     input,
13446                     feedback
13447                 ] 
13448             };  
13449         }
13450         
13451         
13452         if (this.before || this.after) {
13453             
13454             inputblock = {
13455                 cls : 'input-group',
13456                 cn :  [] 
13457             };
13458             if (this.before) {
13459                 inputblock.cn.push({
13460                     tag :'span',
13461                     cls : 'input-group-addon',
13462                     html : this.before
13463                 });
13464             }
13465             
13466             inputblock.cn.push(input);
13467             
13468             if(this.hasFeedback && !this.allowBlank){
13469                 inputblock.cls += ' has-feedback';
13470                 inputblock.cn.push(feedback);
13471             }
13472             
13473             if (this.after) {
13474                 inputblock.cn.push({
13475                     tag :'span',
13476                     cls : 'input-group-addon',
13477                     html : this.after
13478                 });
13479             }
13480             
13481         }
13482         
13483         if (align ==='left' && this.fieldLabel.length) {
13484             cfg.cn = [
13485                 {
13486                     tag: 'label',
13487                     'for' :  id,
13488                     cls : 'control-label',
13489                     html : this.fieldLabel
13490                 },
13491                 {
13492                     cls : "",
13493                     cn: [
13494                         inputblock
13495                     ]
13496                 }
13497
13498             ];
13499             
13500             if(this.labelWidth > 12){
13501                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13502             }
13503
13504             if(this.labelWidth < 13 && this.labelmd == 0){
13505                 this.labelmd = this.labelWidth;
13506             }
13507
13508             if(this.labellg > 0){
13509                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13510                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13511             }
13512
13513             if(this.labelmd > 0){
13514                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13515                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13516             }
13517
13518             if(this.labelsm > 0){
13519                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13520                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13521             }
13522
13523             if(this.labelxs > 0){
13524                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13525                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13526             }
13527             
13528         } else if ( this.fieldLabel.length) {
13529             cfg.cn = [
13530
13531                {
13532                    tag: 'label',
13533                    //cls : 'input-group-addon',
13534                    html : this.fieldLabel
13535
13536                },
13537
13538                inputblock
13539
13540            ];
13541
13542         } else {
13543
13544             cfg.cn = [
13545
13546                 inputblock
13547
13548             ];
13549                 
13550         }
13551         
13552         if (this.disabled) {
13553             input.disabled=true;
13554         }
13555         
13556         return cfg;
13557         
13558     },
13559     /**
13560      * return the real textarea element.
13561      */
13562     inputEl: function ()
13563     {
13564         return this.el.select('textarea.form-control',true).first();
13565     },
13566     
13567     /**
13568      * Clear any invalid styles/messages for this field
13569      */
13570     clearInvalid : function()
13571     {
13572         
13573         if(!this.el || this.preventMark){ // not rendered
13574             return;
13575         }
13576         
13577         var label = this.el.select('label', true).first();
13578         var icon = this.el.select('i.fa-star', true).first();
13579         
13580         if(label && icon){
13581             icon.remove();
13582         }
13583         this.el.removeClass( this.validClass);
13584         this.inputEl().removeClass('is-invalid');
13585          
13586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13587             
13588             var feedback = this.el.select('.form-control-feedback', true).first();
13589             
13590             if(feedback){
13591                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13592             }
13593             
13594         }
13595         
13596         this.fireEvent('valid', this);
13597     },
13598     
13599      /**
13600      * Mark this field as valid
13601      */
13602     markValid : function()
13603     {
13604         if(!this.el  || this.preventMark){ // not rendered
13605             return;
13606         }
13607         
13608         this.el.removeClass([this.invalidClass, this.validClass]);
13609         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13610         
13611         var feedback = this.el.select('.form-control-feedback', true).first();
13612             
13613         if(feedback){
13614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13615         }
13616
13617         if(this.disabled || this.allowBlank){
13618             return;
13619         }
13620         
13621         var label = this.el.select('label', true).first();
13622         var icon = this.el.select('i.fa-star', true).first();
13623         
13624         if(label && icon){
13625             icon.remove();
13626         }
13627         if (Roo.bootstrap.version == 3) {
13628             this.el.addClass(this.validClass);
13629         } else {
13630             this.inputEl().addClass('is-valid');
13631         }
13632         
13633         
13634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13635             
13636             var feedback = this.el.select('.form-control-feedback', true).first();
13637             
13638             if(feedback){
13639                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13640                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13641             }
13642             
13643         }
13644         
13645         this.fireEvent('valid', this);
13646     },
13647     
13648      /**
13649      * Mark this field as invalid
13650      * @param {String} msg The validation message
13651      */
13652     markInvalid : function(msg)
13653     {
13654         if(!this.el  || this.preventMark){ // not rendered
13655             return;
13656         }
13657         
13658         this.el.removeClass([this.invalidClass, this.validClass]);
13659         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13660         
13661         var feedback = this.el.select('.form-control-feedback', true).first();
13662             
13663         if(feedback){
13664             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13665         }
13666
13667         if(this.disabled || this.allowBlank){
13668             return;
13669         }
13670         
13671         var label = this.el.select('label', true).first();
13672         var icon = this.el.select('i.fa-star', true).first();
13673         
13674         if(!this.getValue().length && label && !icon){
13675             this.el.createChild({
13676                 tag : 'i',
13677                 cls : 'text-danger fa fa-lg fa-star',
13678                 tooltip : 'This field is required',
13679                 style : 'margin-right:5px;'
13680             }, label, true);
13681         }
13682         
13683         if (Roo.bootstrap.version == 3) {
13684             this.el.addClass(this.invalidClass);
13685         } else {
13686             this.inputEl().addClass('is-invalid');
13687         }
13688         
13689         // fixme ... this may be depricated need to test..
13690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13691             
13692             var feedback = this.el.select('.form-control-feedback', true).first();
13693             
13694             if(feedback){
13695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13696                 
13697                 if(this.getValue().length || this.forceFeedback){
13698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699                 }
13700                 
13701             }
13702             
13703         }
13704         
13705         this.fireEvent('invalid', this, msg);
13706     }
13707 });
13708
13709  
13710 /*
13711  * - LGPL
13712  *
13713  * trigger field - base class for combo..
13714  * 
13715  */
13716  
13717 /**
13718  * @class Roo.bootstrap.form.TriggerField
13719  * @extends Roo.bootstrap.form.Input
13720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723  * for which you can provide a custom implementation.  For example:
13724  * <pre><code>
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13728 </code></pre>
13729  *
13730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735
13736  * @constructor
13737  * Create a new TriggerField.
13738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739  * to the base TextField)
13740  */
13741 Roo.bootstrap.form.TriggerField = function(config){
13742     this.mimicing = false;
13743     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 };
13745
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13747     /**
13748      * @cfg {String} triggerClass A CSS class to apply to the trigger
13749      */
13750      /**
13751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13752      */
13753     hideTrigger:false,
13754
13755     /**
13756      * @cfg {Boolean} removable (true|false) special filter default false
13757      */
13758     removable : false,
13759     
13760     /** @cfg {Boolean} grow @hide */
13761     /** @cfg {Number} growMin @hide */
13762     /** @cfg {Number} growMax @hide */
13763
13764     /**
13765      * @hide 
13766      * @method
13767      */
13768     autoSize: Roo.emptyFn,
13769     // private
13770     monitorTab : true,
13771     // private
13772     deferHeight : true,
13773
13774     
13775     actionMode : 'wrap',
13776     
13777     caret : false,
13778     
13779     
13780     getAutoCreate : function(){
13781        
13782         var align = this.labelAlign || this.parentLabelAlign();
13783         
13784         var id = Roo.id();
13785         
13786         var cfg = {
13787             cls: 'form-group' //input-group
13788         };
13789         
13790         
13791         var input =  {
13792             tag: 'input',
13793             id : id,
13794             type : this.inputType,
13795             cls : 'form-control',
13796             autocomplete: 'new-password',
13797             placeholder : this.placeholder || '' 
13798             
13799         };
13800         if (this.name) {
13801             input.name = this.name;
13802         }
13803         if (this.size) {
13804             input.cls += ' input-' + this.size;
13805         }
13806         
13807         if (this.disabled) {
13808             input.disabled=true;
13809         }
13810         
13811         var inputblock = input;
13812         
13813         if(this.hasFeedback && !this.allowBlank){
13814             
13815             var feedback = {
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             };
13819             
13820             if(this.removable && !this.editable  ){
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         },
13830                         feedback
13831                     ] 
13832                 };
13833             } else {
13834                 inputblock = {
13835                     cls : 'has-feedback',
13836                     cn :  [
13837                         inputblock,
13838                         feedback
13839                     ] 
13840                 };
13841             }
13842
13843         } else {
13844             if(this.removable && !this.editable ){
13845                 inputblock = {
13846                     cls : 'roo-removable',
13847                     cn :  [
13848                         inputblock,
13849                         {
13850                             tag: 'button',
13851                             html : 'x',
13852                             cls : 'roo-combo-removable-btn close'
13853                         }
13854                     ] 
13855                 };
13856             }
13857         }
13858         
13859         if (this.before || this.after) {
13860             
13861             inputblock = {
13862                 cls : 'input-group',
13863                 cn :  [] 
13864             };
13865             if (this.before) {
13866                 inputblock.cn.push({
13867                     tag :'span',
13868                     cls : 'input-group-addon input-group-prepend input-group-text',
13869                     html : this.before
13870                 });
13871             }
13872             
13873             inputblock.cn.push(input);
13874             
13875             if(this.hasFeedback && !this.allowBlank){
13876                 inputblock.cls += ' has-feedback';
13877                 inputblock.cn.push(feedback);
13878             }
13879             
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon input-group-append input-group-text',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890       
13891         
13892         var ibwrap = inputblock;
13893         
13894         if(this.multiple){
13895             ibwrap = {
13896                 tag: 'ul',
13897                 cls: 'roo-select2-choices',
13898                 cn:[
13899                     {
13900                         tag: 'li',
13901                         cls: 'roo-select2-search-field',
13902                         cn: [
13903
13904                             inputblock
13905                         ]
13906                     }
13907                 ]
13908             };
13909                 
13910         }
13911         
13912         var combobox = {
13913             cls: 'roo-select2-container input-group',
13914             cn: [
13915                  {
13916                     tag: 'input',
13917                     type : 'hidden',
13918                     cls: 'form-hidden-field'
13919                 },
13920                 ibwrap
13921             ]
13922         };
13923         
13924         if(!this.multiple && this.showToggleBtn){
13925             
13926             var caret = {
13927                         tag: 'span',
13928                         cls: 'caret'
13929              };
13930             if (this.caret != false) {
13931                 caret = {
13932                      tag: 'i',
13933                      cls: 'fa fa-' + this.caret
13934                 };
13935                 
13936             }
13937             
13938             combobox.cn.push({
13939                 tag :'span',
13940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13941                 cn : [
13942                     Roo.bootstrap.version == 3 ? caret : '',
13943                     {
13944                         tag: 'span',
13945                         cls: 'combobox-clear',
13946                         cn  : [
13947                             {
13948                                 tag : 'i',
13949                                 cls: 'icon-remove'
13950                             }
13951                         ]
13952                     }
13953                 ]
13954
13955             })
13956         }
13957         
13958         if(this.multiple){
13959             combobox.cls += ' roo-select2-container-multi';
13960         }
13961          var indicator = {
13962             tag : 'i',
13963             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964             tooltip : 'This field is required'
13965         };
13966         if (Roo.bootstrap.version == 4) {
13967             indicator = {
13968                 tag : 'i',
13969                 style : 'display:none'
13970             };
13971         }
13972         
13973         
13974         if (align ==='left' && this.fieldLabel.length) {
13975             
13976             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13977
13978             cfg.cn = [
13979                 indicator,
13980                 {
13981                     tag: 'label',
13982                     'for' :  id,
13983                     cls : 'control-label',
13984                     html : this.fieldLabel
13985
13986                 },
13987                 {
13988                     cls : "", 
13989                     cn: [
13990                         combobox
13991                     ]
13992                 }
13993
13994             ];
13995             
13996             var labelCfg = cfg.cn[1];
13997             var contentCfg = cfg.cn[2];
13998             
13999             if(this.indicatorpos == 'right'){
14000                 cfg.cn = [
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label',
14005                         cn : [
14006                             {
14007                                 tag : 'span',
14008                                 html : this.fieldLabel
14009                             },
14010                             indicator
14011                         ]
14012                     },
14013                     {
14014                         cls : "", 
14015                         cn: [
14016                             combobox
14017                         ]
14018                     }
14019
14020                 ];
14021                 
14022                 labelCfg = cfg.cn[0];
14023                 contentCfg = cfg.cn[1];
14024             }
14025             
14026             if(this.labelWidth > 12){
14027                 labelCfg.style = "width: " + this.labelWidth + 'px';
14028             }
14029             
14030             if(this.labelWidth < 13 && this.labelmd == 0){
14031                 this.labelmd = this.labelWidth;
14032             }
14033             
14034             if(this.labellg > 0){
14035                 labelCfg.cls += ' col-lg-' + this.labellg;
14036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14037             }
14038             
14039             if(this.labelmd > 0){
14040                 labelCfg.cls += ' col-md-' + this.labelmd;
14041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14042             }
14043             
14044             if(this.labelsm > 0){
14045                 labelCfg.cls += ' col-sm-' + this.labelsm;
14046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14047             }
14048             
14049             if(this.labelxs > 0){
14050                 labelCfg.cls += ' col-xs-' + this.labelxs;
14051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14052             }
14053             
14054         } else if ( this.fieldLabel.length) {
14055 //                Roo.log(" label");
14056             cfg.cn = [
14057                 indicator,
14058                {
14059                    tag: 'label',
14060                    //cls : 'input-group-addon',
14061                    html : this.fieldLabel
14062
14063                },
14064
14065                combobox
14066
14067             ];
14068             
14069             if(this.indicatorpos == 'right'){
14070                 
14071                 cfg.cn = [
14072                     {
14073                        tag: 'label',
14074                        cn : [
14075                            {
14076                                tag : 'span',
14077                                html : this.fieldLabel
14078                            },
14079                            indicator
14080                        ]
14081
14082                     },
14083                     combobox
14084
14085                 ];
14086
14087             }
14088
14089         } else {
14090             
14091 //                Roo.log(" no label && no align");
14092                 cfg = combobox
14093                      
14094                 
14095         }
14096         
14097         var settings=this;
14098         ['xs','sm','md','lg'].map(function(size){
14099             if (settings[size]) {
14100                 cfg.cls += ' col-' + size + '-' + settings[size];
14101             }
14102         });
14103         
14104         return cfg;
14105         
14106     },
14107     
14108     
14109     
14110     // private
14111     onResize : function(w, h){
14112 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 //        if(typeof w == 'number'){
14114 //            var x = w - this.trigger.getWidth();
14115 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14116 //            this.trigger.setStyle('left', x+'px');
14117 //        }
14118     },
14119
14120     // private
14121     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14122
14123     // private
14124     getResizeEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     getPositionEl : function(){
14130         return this.inputEl();
14131     },
14132
14133     // private
14134     alignErrorIcon : function(){
14135         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136     },
14137
14138     // private
14139     initEvents : function(){
14140         
14141         this.createList();
14142         
14143         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145         if(!this.multiple && this.showToggleBtn){
14146             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147             if(this.hideTrigger){
14148                 this.trigger.setDisplayed(false);
14149             }
14150             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151         }
14152         
14153         if(this.multiple){
14154             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14155         }
14156         
14157         if(this.removable && !this.editable && !this.tickable){
14158             var close = this.closeTriggerEl();
14159             
14160             if(close){
14161                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162                 close.on('click', this.removeBtnClick, this, close);
14163             }
14164         }
14165         
14166         //this.trigger.addClassOnOver('x-form-trigger-over');
14167         //this.trigger.addClassOnClick('x-form-trigger-click');
14168         
14169         //if(!this.width){
14170         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171         //}
14172     },
14173     
14174     closeTriggerEl : function()
14175     {
14176         var close = this.el.select('.roo-combo-removable-btn', true).first();
14177         return close ? close : false;
14178     },
14179     
14180     removeBtnClick : function(e, h, el)
14181     {
14182         e.preventDefault();
14183         
14184         if(this.fireEvent("remove", this) !== false){
14185             this.reset();
14186             this.fireEvent("afterremove", this)
14187         }
14188     },
14189     
14190     createList : function()
14191     {
14192         this.list = Roo.get(document.body).createChild({
14193             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194             cls: 'typeahead typeahead-long dropdown-menu shadow',
14195             style: 'display:none'
14196         });
14197         
14198         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14199         
14200     },
14201
14202     // private
14203     initTrigger : function(){
14204        
14205     },
14206
14207     // private
14208     onDestroy : function(){
14209         if(this.trigger){
14210             this.trigger.removeAllListeners();
14211           //  this.trigger.remove();
14212         }
14213         //if(this.wrap){
14214         //    this.wrap.remove();
14215         //}
14216         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217     },
14218
14219     // private
14220     onFocus : function(){
14221         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222         /*
14223         if(!this.mimicing){
14224             this.wrap.addClass('x-trigger-wrap-focus');
14225             this.mimicing = true;
14226             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227             if(this.monitorTab){
14228                 this.el.on("keydown", this.checkTab, this);
14229             }
14230         }
14231         */
14232     },
14233
14234     // private
14235     checkTab : function(e){
14236         if(e.getKey() == e.TAB){
14237             this.triggerBlur();
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         // do nothing
14244     },
14245
14246     // private
14247     mimicBlur : function(e, t){
14248         /*
14249         if(!this.wrap.contains(t) && this.validateBlur()){
14250             this.triggerBlur();
14251         }
14252         */
14253     },
14254
14255     // private
14256     triggerBlur : function(){
14257         this.mimicing = false;
14258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259         if(this.monitorTab){
14260             this.el.un("keydown", this.checkTab, this);
14261         }
14262         //this.wrap.removeClass('x-trigger-wrap-focus');
14263         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264     },
14265
14266     // private
14267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268     validateBlur : function(e, t){
14269         return true;
14270     },
14271
14272     // private
14273     onDisable : function(){
14274         this.inputEl().dom.disabled = true;
14275         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276         //if(this.wrap){
14277         //    this.wrap.addClass('x-item-disabled');
14278         //}
14279     },
14280
14281     // private
14282     onEnable : function(){
14283         this.inputEl().dom.disabled = false;
14284         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285         //if(this.wrap){
14286         //    this.el.removeClass('x-item-disabled');
14287         //}
14288     },
14289
14290     // private
14291     onShow : function(){
14292         var ae = this.getActionEl();
14293         
14294         if(ae){
14295             ae.dom.style.display = '';
14296             ae.dom.style.visibility = 'visible';
14297         }
14298     },
14299
14300     // private
14301     
14302     onHide : function(){
14303         var ae = this.getActionEl();
14304         ae.dom.style.display = 'none';
14305     },
14306
14307     /**
14308      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14309      * by an implementing function.
14310      * @method
14311      * @param {EventObject} e
14312      */
14313     onTriggerClick : Roo.emptyFn
14314 });
14315  
14316 /*
14317 * Licence: LGPL
14318 */
14319
14320 /**
14321  * @class Roo.bootstrap.form.CardUploader
14322  * @extends Roo.bootstrap.Button
14323  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324  * @cfg {Number} errorTimeout default 3000
14325  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14326  * @cfg {Array}  html The button text.
14327
14328  *
14329  * @constructor
14330  * Create a new CardUploader
14331  * @param {Object} config The config object
14332  */
14333
14334 Roo.bootstrap.form.CardUploader = function(config){
14335     
14336  
14337     
14338     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14339     
14340     
14341     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14342         return r.data.id
14343      });
14344     
14345      this.addEvents({
14346          // raw events
14347         /**
14348          * @event preview
14349          * When a image is clicked on - and needs to display a slideshow or similar..
14350          * @param {Roo.bootstrap.Card} this
14351          * @param {Object} The image information data 
14352          *
14353          */
14354         'preview' : true,
14355          /**
14356          * @event download
14357          * When a the download link is clicked
14358          * @param {Roo.bootstrap.Card} this
14359          * @param {Object} The image information data  contains 
14360          */
14361         'download' : true
14362         
14363     });
14364 };
14365  
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14367     
14368      
14369     errorTimeout : 3000,
14370      
14371     images : false,
14372    
14373     fileCollection : false,
14374     allowBlank : true,
14375     
14376     getAutoCreate : function()
14377     {
14378         
14379         var cfg =  {
14380             cls :'form-group' ,
14381             cn : [
14382                
14383                 {
14384                     tag: 'label',
14385                    //cls : 'input-group-addon',
14386                     html : this.fieldLabel
14387
14388                 },
14389
14390                 {
14391                     tag: 'input',
14392                     type : 'hidden',
14393                     name : this.name,
14394                     value : this.value,
14395                     cls : 'd-none  form-control'
14396                 },
14397                 
14398                 {
14399                     tag: 'input',
14400                     multiple : 'multiple',
14401                     type : 'file',
14402                     cls : 'd-none  roo-card-upload-selector'
14403                 },
14404                 
14405                 {
14406                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14407                 },
14408                 {
14409                     cls : 'card-columns roo-card-uploader-container'
14410                 }
14411
14412             ]
14413         };
14414            
14415          
14416         return cfg;
14417     },
14418     
14419     getChildContainer : function() /// what children are added to.
14420     {
14421         return this.containerEl;
14422     },
14423    
14424     getButtonContainer : function() /// what children are added to.
14425     {
14426         return this.el.select(".roo-card-uploader-button-container").first();
14427     },
14428    
14429     initEvents : function()
14430     {
14431         
14432         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433         
14434         var t = this;
14435         this.addxtype({
14436             xns: Roo.bootstrap,
14437
14438             xtype : 'Button',
14439             container_method : 'getButtonContainer' ,            
14440             html :  this.html, // fix changable?
14441             cls : 'w-100 ',
14442             listeners : {
14443                 'click' : function(btn, e) {
14444                     t.onClick(e);
14445                 }
14446             }
14447         });
14448         
14449         
14450         
14451         
14452         this.urlAPI = (window.createObjectURL && window) || 
14453                                 (window.URL && URL.revokeObjectURL && URL) || 
14454                                 (window.webkitURL && webkitURL);
14455                         
14456          
14457          
14458          
14459         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460         
14461         this.selectorEl.on('change', this.onFileSelected, this);
14462         if (this.images) {
14463             var t = this;
14464             this.images.forEach(function(img) {
14465                 t.addCard(img)
14466             });
14467             this.images = false;
14468         }
14469         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470          
14471        
14472     },
14473     
14474    
14475     onClick : function(e)
14476     {
14477         e.preventDefault();
14478          
14479         this.selectorEl.dom.click();
14480          
14481     },
14482     
14483     onFileSelected : function(e)
14484     {
14485         e.preventDefault();
14486         
14487         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488             return;
14489         }
14490         
14491         Roo.each(this.selectorEl.dom.files, function(file){    
14492             this.addFile(file);
14493         }, this);
14494          
14495     },
14496     
14497       
14498     
14499       
14500     
14501     addFile : function(file)
14502     {
14503            
14504         if(typeof(file) === 'string'){
14505             throw "Add file by name?"; // should not happen
14506             return;
14507         }
14508         
14509         if(!file || !this.urlAPI){
14510             return;
14511         }
14512         
14513         // file;
14514         // file.type;
14515         
14516         var _this = this;
14517         
14518         
14519         var url = _this.urlAPI.createObjectURL( file);
14520            
14521         this.addCard({
14522             id : Roo.bootstrap.form.CardUploader.ID--,
14523             is_uploaded : false,
14524             src : url,
14525             srcfile : file,
14526             title : file.name,
14527             mimetype : file.type,
14528             preview : false,
14529             is_deleted : 0
14530         });
14531         
14532     },
14533     
14534     /**
14535      * addCard - add an Attachment to the uploader
14536      * @param data - the data about the image to upload
14537      *
14538      * {
14539           id : 123
14540           title : "Title of file",
14541           is_uploaded : false,
14542           src : "http://.....",
14543           srcfile : { the File upload object },
14544           mimetype : file.type,
14545           preview : false,
14546           is_deleted : 0
14547           .. any other data...
14548         }
14549      *
14550      * 
14551     */
14552     
14553     addCard : function (data)
14554     {
14555         // hidden input element?
14556         // if the file is not an image...
14557         //then we need to use something other that and header_image
14558         var t = this;
14559         //   remove.....
14560         var footer = [
14561             {
14562                 xns : Roo.bootstrap,
14563                 xtype : 'CardFooter',
14564                  items: [
14565                     {
14566                         xns : Roo.bootstrap,
14567                         xtype : 'Element',
14568                         cls : 'd-flex',
14569                         items : [
14570                             
14571                             {
14572                                 xns : Roo.bootstrap,
14573                                 xtype : 'Button',
14574                                 html : String.format("<small>{0}</small>", data.title),
14575                                 cls : 'col-10 text-left',
14576                                 size: 'sm',
14577                                 weight: 'link',
14578                                 fa : 'download',
14579                                 listeners : {
14580                                     click : function() {
14581                                      
14582                                         t.fireEvent( "download", t, data );
14583                                     }
14584                                 }
14585                             },
14586                           
14587                             {
14588                                 xns : Roo.bootstrap,
14589                                 xtype : 'Button',
14590                                 style: 'max-height: 28px; ',
14591                                 size : 'sm',
14592                                 weight: 'danger',
14593                                 cls : 'col-2',
14594                                 fa : 'times',
14595                                 listeners : {
14596                                     click : function() {
14597                                         t.removeCard(data.id)
14598                                     }
14599                                 }
14600                             }
14601                         ]
14602                     }
14603                     
14604                 ] 
14605             }
14606             
14607         ];
14608         
14609         var cn = this.addxtype(
14610             {
14611                  
14612                 xns : Roo.bootstrap,
14613                 xtype : 'Card',
14614                 closeable : true,
14615                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14617                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14618                 data : data,
14619                 html : false,
14620                  
14621                 items : footer,
14622                 initEvents : function() {
14623                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14624                     var card = this;
14625                     this.imgEl = this.el.select('.card-img-top').first();
14626                     if (this.imgEl) {
14627                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628                         this.imgEl.set({ 'pointer' : 'cursor' });
14629                                   
14630                     }
14631                     this.getCardFooter().addClass('p-1');
14632                     
14633                   
14634                 }
14635                 
14636             }
14637         );
14638         // dont' really need ot update items.
14639         // this.items.push(cn);
14640         this.fileCollection.add(cn);
14641         
14642         if (!data.srcfile) {
14643             this.updateInput();
14644             return;
14645         }
14646             
14647         var _t = this;
14648         var reader = new FileReader();
14649         reader.addEventListener("load", function() {  
14650             data.srcdata =  reader.result;
14651             _t.updateInput();
14652         });
14653         reader.readAsDataURL(data.srcfile);
14654         
14655         
14656         
14657     },
14658     removeCard : function(id)
14659     {
14660         
14661         var card  = this.fileCollection.get(id);
14662         card.data.is_deleted = 1;
14663         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664         //this.fileCollection.remove(card);
14665         //this.items = this.items.filter(function(e) { return e != card });
14666         // dont' really need ot update items.
14667         card.el.dom.parentNode.removeChild(card.el.dom);
14668         this.updateInput();
14669
14670         
14671     },
14672     reset: function()
14673     {
14674         this.fileCollection.each(function(card) {
14675             if (card.el.dom && card.el.dom.parentNode) {
14676                 card.el.dom.parentNode.removeChild(card.el.dom);
14677             }
14678         });
14679         this.fileCollection.clear();
14680         this.updateInput();
14681     },
14682     
14683     updateInput : function()
14684     {
14685          var data = [];
14686         this.fileCollection.each(function(e) {
14687             data.push(e.data);
14688             
14689         });
14690         this.inputEl().dom.value = JSON.stringify(data);
14691         
14692         
14693         
14694     }
14695     
14696     
14697 });
14698
14699
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711
14712 /**
14713  * @class Roo.data.SortTypes
14714  * @static
14715  * Defines the default sorting (casting?) comparison functions used when sorting data.
14716  */
14717 Roo.data.SortTypes = {
14718     /**
14719      * Default sort that does nothing
14720      * @param {Mixed} s The value being converted
14721      * @return {Mixed} The comparison value
14722      */
14723     none : function(s){
14724         return s;
14725     },
14726     
14727     /**
14728      * The regular expression used to strip tags
14729      * @type {RegExp}
14730      * @property
14731      */
14732     stripTagsRE : /<\/?[^>]+>/gi,
14733     
14734     /**
14735      * Strips all HTML tags to sort on text only
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asText : function(s){
14740         return String(s).replace(this.stripTagsRE, "");
14741     },
14742     
14743     /**
14744      * Strips all HTML tags to sort on text only - Case insensitive
14745      * @param {Mixed} s The value being converted
14746      * @return {String} The comparison value
14747      */
14748     asUCText : function(s){
14749         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750     },
14751     
14752     /**
14753      * Case insensitive string
14754      * @param {Mixed} s The value being converted
14755      * @return {String} The comparison value
14756      */
14757     asUCString : function(s) {
14758         return String(s).toUpperCase();
14759     },
14760     
14761     /**
14762      * Date sorting
14763      * @param {Mixed} s The value being converted
14764      * @return {Number} The comparison value
14765      */
14766     asDate : function(s) {
14767         if(!s){
14768             return 0;
14769         }
14770         if(s instanceof Date){
14771             return s.getTime();
14772         }
14773         return Date.parse(String(s));
14774     },
14775     
14776     /**
14777      * Float sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Float} The comparison value
14780      */
14781     asFloat : function(s) {
14782         var val = parseFloat(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     },
14788     
14789     /**
14790      * Integer sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asInt : function(s) {
14795         var val = parseInt(String(s).replace(/,/g, ""));
14796         if(isNaN(val)) {
14797             val = 0;
14798         }
14799         return val;
14800     }
14801 };/*
14802  * Based on:
14803  * Ext JS Library 1.1.1
14804  * Copyright(c) 2006-2007, Ext JS, LLC.
14805  *
14806  * Originally Released Under LGPL - original licence link has changed is not relivant.
14807  *
14808  * Fork - LGPL
14809  * <script type="text/javascript">
14810  */
14811
14812 /**
14813 * @class Roo.data.Record
14814  * Instances of this class encapsulate both record <em>definition</em> information, and record
14815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816  * to access Records cached in an {@link Roo.data.Store} object.<br>
14817  * <p>
14818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14820  * objects.<br>
14821  * <p>
14822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823  * @constructor
14824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825  * {@link #create}. The parameters are the same.
14826  * @param {Array} data An associative Array of data values keyed by the field name.
14827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829  * not specified an integer id is generated.
14830  */
14831 Roo.data.Record = function(data, id){
14832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14833     this.data = data;
14834 };
14835
14836 /**
14837  * Generate a constructor for a specific record layout.
14838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840  * Each field definition object may contain the following properties: <ul>
14841  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848  * this may be omitted.</p></li>
14849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850  * <ul><li>auto (Default, implies no conversion)</li>
14851  * <li>string</li>
14852  * <li>int</li>
14853  * <li>float</li>
14854  * <li>boolean</li>
14855  * <li>date</li></ul></p></li>
14856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859  * by the Reader into an object that will be stored in the Record. It is passed the
14860  * following parameters:<ul>
14861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862  * </ul></p></li>
14863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864  * </ul>
14865  * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867     {name: 'title', mapping: 'topic_title'},
14868     {name: 'author', mapping: 'username'},
14869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871     {name: 'lastPoster', mapping: 'user2'},
14872     {name: 'excerpt', mapping: 'post_text'}
14873 );
14874
14875 var myNewRecord = new TopicRecord({
14876     title: 'Do my job please',
14877     author: 'noobie',
14878     totalPosts: 1,
14879     lastPost: new Date(),
14880     lastPoster: 'Animal',
14881     excerpt: 'No way dude!'
14882 });
14883 myStore.add(myNewRecord);
14884 </code></pre>
14885  * @method create
14886  * @static
14887  */
14888 Roo.data.Record.create = function(o){
14889     var f = function(){
14890         f.superclass.constructor.apply(this, arguments);
14891     };
14892     Roo.extend(f, Roo.data.Record);
14893     var p = f.prototype;
14894     p.fields = new Roo.util.MixedCollection(false, function(field){
14895         return field.name;
14896     });
14897     for(var i = 0, len = o.length; i < len; i++){
14898         p.fields.add(new Roo.data.Field(o[i]));
14899     }
14900     f.getField = function(name){
14901         return p.fields.get(name);  
14902     };
14903     return f;
14904 };
14905
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14910
14911 Roo.data.Record.prototype = {
14912     /**
14913      * Readonly flag - true if this record has been modified.
14914      * @type Boolean
14915      */
14916     dirty : false,
14917     editing : false,
14918     error: null,
14919     modified: null,
14920
14921     // private
14922     join : function(store){
14923         this.store = store;
14924     },
14925
14926     /**
14927      * Set the named field to the specified value.
14928      * @param {String} name The name of the field to set.
14929      * @param {Object} value The value to set the field to.
14930      */
14931     set : function(name, value){
14932         if(this.data[name] == value){
14933             return;
14934         }
14935         this.dirty = true;
14936         if(!this.modified){
14937             this.modified = {};
14938         }
14939         if(typeof this.modified[name] == 'undefined'){
14940             this.modified[name] = this.data[name];
14941         }
14942         this.data[name] = value;
14943         if(!this.editing && this.store){
14944             this.store.afterEdit(this);
14945         }       
14946     },
14947
14948     /**
14949      * Get the value of the named field.
14950      * @param {String} name The name of the field to get the value of.
14951      * @return {Object} The value of the field.
14952      */
14953     get : function(name){
14954         return this.data[name]; 
14955     },
14956
14957     // private
14958     beginEdit : function(){
14959         this.editing = true;
14960         this.modified = {}; 
14961     },
14962
14963     // private
14964     cancelEdit : function(){
14965         this.editing = false;
14966         delete this.modified;
14967     },
14968
14969     // private
14970     endEdit : function(){
14971         this.editing = false;
14972         if(this.dirty && this.store){
14973             this.store.afterEdit(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Rejects all changes made to the Record since either creation, or the last commit operation.
14980      * Modified fields are reverted to their original values.
14981      * <p>
14982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983      * of reject operations.
14984      */
14985     reject : function(){
14986         var m = this.modified;
14987         for(var n in m){
14988             if(typeof m[n] != "function"){
14989                 this.data[n] = m[n];
14990             }
14991         }
14992         this.dirty = false;
14993         delete this.modified;
14994         this.editing = false;
14995         if(this.store){
14996             this.store.afterReject(this);
14997         }
14998     },
14999
15000     /**
15001      * Usually called by the {@link Roo.data.Store} which owns the Record.
15002      * Commits all changes made to the Record since either creation, or the last commit operation.
15003      * <p>
15004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005      * of commit operations.
15006      */
15007     commit : function(){
15008         this.dirty = false;
15009         delete this.modified;
15010         this.editing = false;
15011         if(this.store){
15012             this.store.afterCommit(this);
15013         }
15014     },
15015
15016     // private
15017     hasError : function(){
15018         return this.error != null;
15019     },
15020
15021     // private
15022     clearError : function(){
15023         this.error = null;
15024     },
15025
15026     /**
15027      * Creates a copy of this record.
15028      * @param {String} id (optional) A new record id if you don't want to use this record's id
15029      * @return {Record}
15030      */
15031     copy : function(newId) {
15032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033     }
15034 };/*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044
15045
15046
15047 /**
15048  * @class Roo.data.Store
15049  * @extends Roo.util.Observable
15050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052  * <p>
15053  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15054  * has no knowledge of the format of the data returned by the Proxy.<br>
15055  * <p>
15056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057  * instances from the data object. These records are cached and made available through accessor functions.
15058  * @constructor
15059  * Creates a new Store.
15060  * @param {Object} config A config object containing the objects needed for the Store to access data,
15061  * and read the data into Records.
15062  */
15063 Roo.data.Store = function(config){
15064     this.data = new Roo.util.MixedCollection(false);
15065     this.data.getKey = function(o){
15066         return o.id;
15067     };
15068     this.baseParams = {};
15069     // private
15070     this.paramNames = {
15071         "start" : "start",
15072         "limit" : "limit",
15073         "sort" : "sort",
15074         "dir" : "dir",
15075         "multisort" : "_multisort"
15076     };
15077
15078     if(config && config.data){
15079         this.inlineData = config.data;
15080         delete config.data;
15081     }
15082
15083     Roo.apply(this, config);
15084     
15085     if(this.reader){ // reader passed
15086         this.reader = Roo.factory(this.reader, Roo.data);
15087         this.reader.xmodule = this.xmodule || false;
15088         if(!this.recordType){
15089             this.recordType = this.reader.recordType;
15090         }
15091         if(this.reader.onMetaChange){
15092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093         }
15094     }
15095
15096     if(this.recordType){
15097         this.fields = this.recordType.prototype.fields;
15098     }
15099     this.modified = [];
15100
15101     this.addEvents({
15102         /**
15103          * @event datachanged
15104          * Fires when the data cache has changed, and a widget which is using this Store
15105          * as a Record cache should refresh its view.
15106          * @param {Store} this
15107          */
15108         datachanged : true,
15109         /**
15110          * @event metachange
15111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112          * @param {Store} this
15113          * @param {Object} meta The JSON metadata
15114          */
15115         metachange : true,
15116         /**
15117          * @event add
15118          * Fires when Records have been added to the Store
15119          * @param {Store} this
15120          * @param {Roo.data.Record[]} records The array of Records added
15121          * @param {Number} index The index at which the record(s) were added
15122          */
15123         add : true,
15124         /**
15125          * @event remove
15126          * Fires when a Record has been removed from the Store
15127          * @param {Store} this
15128          * @param {Roo.data.Record} record The Record that was removed
15129          * @param {Number} index The index at which the record was removed
15130          */
15131         remove : true,
15132         /**
15133          * @event update
15134          * Fires when a Record has been updated
15135          * @param {Store} this
15136          * @param {Roo.data.Record} record The Record that was updated
15137          * @param {String} operation The update operation being performed.  Value may be one of:
15138          * <pre><code>
15139  Roo.data.Record.EDIT
15140  Roo.data.Record.REJECT
15141  Roo.data.Record.COMMIT
15142          * </code></pre>
15143          */
15144         update : true,
15145         /**
15146          * @event clear
15147          * Fires when the data cache has been cleared.
15148          * @param {Store} this
15149          */
15150         clear : true,
15151         /**
15152          * @event beforeload
15153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15154          * the load action will be canceled.
15155          * @param {Store} this
15156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157          */
15158         beforeload : true,
15159         /**
15160          * @event beforeloadadd
15161          * Fires after a new set of Records has been loaded.
15162          * @param {Store} this
15163          * @param {Roo.data.Record[]} records The Records that were loaded
15164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165          */
15166         beforeloadadd : true,
15167         /**
15168          * @event load
15169          * Fires after a new set of Records has been loaded, before they are added to the store.
15170          * @param {Store} this
15171          * @param {Roo.data.Record[]} records The Records that were loaded
15172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173          * @params {Object} return from reader
15174          */
15175         load : true,
15176         /**
15177          * @event loadexception
15178          * Fires if an exception occurs in the Proxy during loading.
15179          * Called with the signature of the Proxy's "loadexception" event.
15180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15181          * 
15182          * @param {Proxy} 
15183          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15184          * @param {Object} load options 
15185          * @param {Object} jsonData from your request (normally this contains the Exception)
15186          */
15187         loadexception : true
15188     });
15189     
15190     if(this.proxy){
15191         this.proxy = Roo.factory(this.proxy, Roo.data);
15192         this.proxy.xmodule = this.xmodule || false;
15193         this.relayEvents(this.proxy,  ["loadexception"]);
15194     }
15195     this.sortToggle = {};
15196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197
15198     Roo.data.Store.superclass.constructor.call(this);
15199
15200     if(this.inlineData){
15201         this.loadData(this.inlineData);
15202         delete this.inlineData;
15203     }
15204 };
15205
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207      /**
15208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15209     * without a remote query - used by combo/forms at present.
15210     */
15211     
15212     /**
15213     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15214     */
15215     /**
15216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15217     */
15218     /**
15219     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15221     */
15222     /**
15223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224     * on any HTTP request
15225     */
15226     /**
15227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15228     */
15229     /**
15230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231     */
15232     multiSort: false,
15233     /**
15234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236     */
15237     remoteSort : false,
15238
15239     /**
15240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241      * loaded or when a record is removed. (defaults to false).
15242     */
15243     pruneModifiedRecords : false,
15244
15245     // private
15246     lastOptions : null,
15247
15248     /**
15249      * Add Records to the Store and fires the add event.
15250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251      */
15252     add : function(records){
15253         records = [].concat(records);
15254         for(var i = 0, len = records.length; i < len; i++){
15255             records[i].join(this);
15256         }
15257         var index = this.data.length;
15258         this.data.addAll(records);
15259         this.fireEvent("add", this, records, index);
15260     },
15261
15262     /**
15263      * Remove a Record from the Store and fires the remove event.
15264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265      */
15266     remove : function(record){
15267         var index = this.data.indexOf(record);
15268         this.data.removeAt(index);
15269  
15270         if(this.pruneModifiedRecords){
15271             this.modified.remove(record);
15272         }
15273         this.fireEvent("remove", this, record, index);
15274     },
15275
15276     /**
15277      * Remove all Records from the Store and fires the clear event.
15278      */
15279     removeAll : function(){
15280         this.data.clear();
15281         if(this.pruneModifiedRecords){
15282             this.modified = [];
15283         }
15284         this.fireEvent("clear", this);
15285     },
15286
15287     /**
15288      * Inserts Records to the Store at the given index and fires the add event.
15289      * @param {Number} index The start index at which to insert the passed Records.
15290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291      */
15292     insert : function(index, records){
15293         records = [].concat(records);
15294         for(var i = 0, len = records.length; i < len; i++){
15295             this.data.insert(index, records[i]);
15296             records[i].join(this);
15297         }
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Get the index within the cache of the passed Record.
15303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304      * @return {Number} The index of the passed Record. Returns -1 if not found.
15305      */
15306     indexOf : function(record){
15307         return this.data.indexOf(record);
15308     },
15309
15310     /**
15311      * Get the index within the cache of the Record with the passed id.
15312      * @param {String} id The id of the Record to find.
15313      * @return {Number} The index of the Record. Returns -1 if not found.
15314      */
15315     indexOfId : function(id){
15316         return this.data.indexOfKey(id);
15317     },
15318
15319     /**
15320      * Get the Record with the specified id.
15321      * @param {String} id The id of the Record to find.
15322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323      */
15324     getById : function(id){
15325         return this.data.key(id);
15326     },
15327
15328     /**
15329      * Get the Record at the specified index.
15330      * @param {Number} index The index of the Record to find.
15331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332      */
15333     getAt : function(index){
15334         return this.data.itemAt(index);
15335     },
15336
15337     /**
15338      * Returns a range of Records between specified indices.
15339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341      * @return {Roo.data.Record[]} An array of Records
15342      */
15343     getRange : function(start, end){
15344         return this.data.getRange(start, end);
15345     },
15346
15347     // private
15348     storeOptions : function(o){
15349         o = Roo.apply({}, o);
15350         delete o.callback;
15351         delete o.scope;
15352         this.lastOptions = o;
15353     },
15354
15355     /**
15356      * Loads the Record cache from the configured Proxy using the configured Reader.
15357      * <p>
15358      * If using remote paging, then the first load call must specify the <em>start</em>
15359      * and <em>limit</em> properties in the options.params property to establish the initial
15360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361      * <p>
15362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363      * and this call will return before the new data has been loaded. Perform any post-processing
15364      * in a callback function, or in a "load" event handler.</strong>
15365      * <p>
15366      * @param {Object} options An object containing properties which control loading options:<ul>
15367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15369      * <pre>
15370                 {
15371                     data : data,  // array of key=>value data like JsonReader
15372                     total : data.length,
15373                     success : true
15374                     
15375                 }
15376         </pre>
15377             }.</li>
15378      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379      * passed the following arguments:<ul>
15380      * <li>r : Roo.data.Record[]</li>
15381      * <li>options: Options object from the load call</li>
15382      * <li>success: Boolean success indicator</li></ul></li>
15383      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15385      * </ul>
15386      */
15387     load : function(options){
15388         options = options || {};
15389         if(this.fireEvent("beforeload", this, options) !== false){
15390             this.storeOptions(options);
15391             var p = Roo.apply(options.params || {}, this.baseParams);
15392             // if meta was not loaded from remote source.. try requesting it.
15393             if (!this.reader.metaFromRemote) {
15394                 p._requestMeta = 1;
15395             }
15396             if(this.sortInfo && this.remoteSort){
15397                 var pn = this.paramNames;
15398                 p[pn["sort"]] = this.sortInfo.field;
15399                 p[pn["dir"]] = this.sortInfo.direction;
15400             }
15401             if (this.multiSort) {
15402                 var pn = this.paramNames;
15403                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15404             }
15405             
15406             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15407         }
15408     },
15409
15410     /**
15411      * Reloads the Record cache from the configured Proxy using the configured Reader and
15412      * the options from the last load operation performed.
15413      * @param {Object} options (optional) An object containing properties which may override the options
15414      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415      * the most recently used options are reused).
15416      */
15417     reload : function(options){
15418         this.load(Roo.applyIf(options||{}, this.lastOptions));
15419     },
15420
15421     // private
15422     // Called as a callback by the Reader during a load operation.
15423     loadRecords : function(o, options, success){
15424          
15425         if(!o){
15426             if(success !== false){
15427                 this.fireEvent("load", this, [], options, o);
15428             }
15429             if(options.callback){
15430                 options.callback.call(options.scope || this, [], options, false);
15431             }
15432             return;
15433         }
15434         // if data returned failure - throw an exception.
15435         if (o.success === false) {
15436             // show a message if no listener is registered.
15437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439             }
15440             // loadmask wil be hooked into this..
15441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15442             return;
15443         }
15444         var r = o.records, t = o.totalRecords || r.length;
15445         
15446         this.fireEvent("beforeloadadd", this, r, options, o);
15447         
15448         if(!options || options.add !== true){
15449             if(this.pruneModifiedRecords){
15450                 this.modified = [];
15451             }
15452             for(var i = 0, len = r.length; i < len; i++){
15453                 r[i].join(this);
15454             }
15455             if(this.snapshot){
15456                 this.data = this.snapshot;
15457                 delete this.snapshot;
15458             }
15459             this.data.clear();
15460             this.data.addAll(r);
15461             this.totalLength = t;
15462             this.applySort();
15463             this.fireEvent("datachanged", this);
15464         }else{
15465             this.totalLength = Math.max(t, this.data.length+r.length);
15466             this.add(r);
15467         }
15468         
15469         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470                 
15471             var e = new Roo.data.Record({});
15472
15473             e.set(this.parent.displayField, this.parent.emptyTitle);
15474             e.set(this.parent.valueField, '');
15475
15476             this.insert(0, e);
15477         }
15478             
15479         this.fireEvent("load", this, r, options, o);
15480         if(options.callback){
15481             options.callback.call(options.scope || this, r, options, true);
15482         }
15483     },
15484
15485
15486     /**
15487      * Loads data from a passed data block. A Reader which understands the format of the data
15488      * must have been configured in the constructor.
15489      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15490      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492      */
15493     loadData : function(o, append){
15494         var r = this.reader.readRecords(o);
15495         this.loadRecords(r, {add: append}, true);
15496     },
15497     
15498      /**
15499      * using 'cn' the nested child reader read the child array into it's child stores.
15500      * @param {Object} rec The record with a 'children array
15501      */
15502     loadDataFromChildren : function(rec)
15503     {
15504         this.loadData(this.reader.toLoadData(rec));
15505     },
15506     
15507
15508     /**
15509      * Gets the number of cached records.
15510      * <p>
15511      * <em>If using paging, this may not be the total size of the dataset. If the data object
15512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513      * the data set size</em>
15514      */
15515     getCount : function(){
15516         return this.data.length || 0;
15517     },
15518
15519     /**
15520      * Gets the total number of records in the dataset as returned by the server.
15521      * <p>
15522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523      * the dataset size</em>
15524      */
15525     getTotalCount : function(){
15526         return this.totalLength || 0;
15527     },
15528
15529     /**
15530      * Returns the sort state of the Store as an object with two properties:
15531      * <pre><code>
15532  field {String} The name of the field by which the Records are sorted
15533  direction {String} The sort order, "ASC" or "DESC"
15534      * </code></pre>
15535      */
15536     getSortState : function(){
15537         return this.sortInfo;
15538     },
15539
15540     // private
15541     applySort : function(){
15542         if(this.sortInfo && !this.remoteSort){
15543             var s = this.sortInfo, f = s.field;
15544             var st = this.fields.get(f).sortType;
15545             var fn = function(r1, r2){
15546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548             };
15549             this.data.sort(s.direction, fn);
15550             if(this.snapshot && this.snapshot != this.data){
15551                 this.snapshot.sort(s.direction, fn);
15552             }
15553         }
15554     },
15555
15556     /**
15557      * Sets the default sort column and order to be used by the next load operation.
15558      * @param {String} fieldName The name of the field to sort by.
15559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560      */
15561     setDefaultSort : function(field, dir){
15562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563     },
15564
15565     /**
15566      * Sort the Records.
15567      * If remote sorting is used, the sort is performed on the server, and the cache is
15568      * reloaded. If local sorting is used, the cache is sorted internally.
15569      * @param {String} fieldName The name of the field to sort by.
15570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571      */
15572     sort : function(fieldName, dir){
15573         var f = this.fields.get(fieldName);
15574         if(!dir){
15575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576             
15577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15579             }else{
15580                 dir = f.sortDir;
15581             }
15582         }
15583         this.sortToggle[f.name] = dir;
15584         this.sortInfo = {field: f.name, direction: dir};
15585         if(!this.remoteSort){
15586             this.applySort();
15587             this.fireEvent("datachanged", this);
15588         }else{
15589             this.load(this.lastOptions);
15590         }
15591     },
15592
15593     /**
15594      * Calls the specified function for each of the Records in the cache.
15595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596      * Returning <em>false</em> aborts and exits the iteration.
15597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598      */
15599     each : function(fn, scope){
15600         this.data.each(fn, scope);
15601     },
15602
15603     /**
15604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15605      * (e.g., during paging).
15606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607      */
15608     getModifiedRecords : function(){
15609         return this.modified;
15610     },
15611
15612     // private
15613     createFilterFn : function(property, value, anyMatch){
15614         if(!value.exec){ // not a regex
15615             value = String(value);
15616             if(value.length == 0){
15617                 return false;
15618             }
15619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620         }
15621         return function(r){
15622             return value.test(r.data[property]);
15623         };
15624     },
15625
15626     /**
15627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628      * @param {String} property A field on your records
15629      * @param {Number} start The record index to start at (defaults to 0)
15630      * @param {Number} end The last record index to include (defaults to length - 1)
15631      * @return {Number} The sum
15632      */
15633     sum : function(property, start, end){
15634         var rs = this.data.items, v = 0;
15635         start = start || 0;
15636         end = (end || end === 0) ? end : rs.length-1;
15637
15638         for(var i = start; i <= end; i++){
15639             v += (rs[i].data[property] || 0);
15640         }
15641         return v;
15642     },
15643
15644     /**
15645      * Filter the records by a specified property.
15646      * @param {String} field A field on your records
15647      * @param {String/RegExp} value Either a string that the field
15648      * should start with or a RegExp to test against the field
15649      * @param {Boolean} anyMatch True to match any part not just the beginning
15650      */
15651     filter : function(property, value, anyMatch){
15652         var fn = this.createFilterFn(property, value, anyMatch);
15653         return fn ? this.filterBy(fn) : this.clearFilter();
15654     },
15655
15656     /**
15657      * Filter by a function. The specified function will be called with each
15658      * record in this data source. If the function returns true the record is included,
15659      * otherwise it is filtered.
15660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661      * @param {Object} scope (optional) The scope of the function (defaults to this)
15662      */
15663     filterBy : function(fn, scope){
15664         this.snapshot = this.snapshot || this.data;
15665         this.data = this.queryBy(fn, scope||this);
15666         this.fireEvent("datachanged", this);
15667     },
15668
15669     /**
15670      * Query the records by a specified property.
15671      * @param {String} field A field on your records
15672      * @param {String/RegExp} value Either a string that the field
15673      * should start with or a RegExp to test against the field
15674      * @param {Boolean} anyMatch True to match any part not just the beginning
15675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676      */
15677     query : function(property, value, anyMatch){
15678         var fn = this.createFilterFn(property, value, anyMatch);
15679         return fn ? this.queryBy(fn) : this.data.clone();
15680     },
15681
15682     /**
15683      * Query by a function. The specified function will be called with each
15684      * record in this data source. If the function returns true the record is included
15685      * in the results.
15686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687      * @param {Object} scope (optional) The scope of the function (defaults to this)
15688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689      **/
15690     queryBy : function(fn, scope){
15691         var data = this.snapshot || this.data;
15692         return data.filterBy(fn, scope||this);
15693     },
15694
15695     /**
15696      * Collects unique values for a particular dataIndex from this store.
15697      * @param {String} dataIndex The property to collect
15698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700      * @return {Array} An array of the unique values
15701      **/
15702     collect : function(dataIndex, allowNull, bypassFilter){
15703         var d = (bypassFilter === true && this.snapshot) ?
15704                 this.snapshot.items : this.data.items;
15705         var v, sv, r = [], l = {};
15706         for(var i = 0, len = d.length; i < len; i++){
15707             v = d[i].data[dataIndex];
15708             sv = String(v);
15709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710                 l[sv] = true;
15711                 r[r.length] = v;
15712             }
15713         }
15714         return r;
15715     },
15716
15717     /**
15718      * Revert to a view of the Record cache with no filtering applied.
15719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720      */
15721     clearFilter : function(suppressEvent){
15722         if(this.snapshot && this.snapshot != this.data){
15723             this.data = this.snapshot;
15724             delete this.snapshot;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("datachanged", this);
15727             }
15728         }
15729     },
15730
15731     // private
15732     afterEdit : function(record){
15733         if(this.modified.indexOf(record) == -1){
15734             this.modified.push(record);
15735         }
15736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737     },
15738     
15739     // private
15740     afterReject : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743     },
15744
15745     // private
15746     afterCommit : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749     },
15750
15751     /**
15752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754      */
15755     commitChanges : function(){
15756         var m = this.modified.slice(0);
15757         this.modified = [];
15758         for(var i = 0, len = m.length; i < len; i++){
15759             m[i].commit();
15760         }
15761     },
15762
15763     /**
15764      * Cancel outstanding changes on all changed records.
15765      */
15766     rejectChanges : function(){
15767         var m = this.modified.slice(0);
15768         this.modified = [];
15769         for(var i = 0, len = m.length; i < len; i++){
15770             m[i].reject();
15771         }
15772     },
15773
15774     onMetaChange : function(meta, rtype, o){
15775         this.recordType = rtype;
15776         this.fields = rtype.prototype.fields;
15777         delete this.snapshot;
15778         this.sortInfo = meta.sortInfo || this.sortInfo;
15779         this.modified = [];
15780         this.fireEvent('metachange', this, this.reader.meta);
15781     },
15782     
15783     moveIndex : function(data, type)
15784     {
15785         var index = this.indexOf(data);
15786         
15787         var newIndex = index + type;
15788         
15789         this.remove(data);
15790         
15791         this.insert(newIndex, data);
15792         
15793     }
15794 });/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806  * @class Roo.data.SimpleStore
15807  * @extends Roo.data.Store
15808  * Small helper class to make creating Stores from Array data easier.
15809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810  * @cfg {Array} fields An array of field definition objects, or field name strings.
15811  * @cfg {Object} an existing reader (eg. copied from another store)
15812  * @cfg {Array} data The multi-dimensional array of data
15813  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15814  * @cfg {Roo.data.Reader} reader  [not-required] 
15815  * @constructor
15816  * @param {Object} config
15817  */
15818 Roo.data.SimpleStore = function(config)
15819 {
15820     Roo.data.SimpleStore.superclass.constructor.call(this, {
15821         isLocal : true,
15822         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15823                 id: config.id
15824             },
15825             Roo.data.Record.create(config.fields)
15826         ),
15827         proxy : new Roo.data.MemoryProxy(config.data)
15828     });
15829     this.load();
15830 };
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843 /**
15844  * @extends Roo.data.Store
15845  * @class Roo.data.JsonStore
15846  * Small helper class to make creating Stores for JSON data easier. <br/>
15847 <pre><code>
15848 var store = new Roo.data.JsonStore({
15849     url: 'get-images.php',
15850     root: 'images',
15851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15852 });
15853 </code></pre>
15854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855  * JsonReader and HttpProxy (unless inline data is provided).</b>
15856  * @cfg {Array} fields An array of field definition objects, or field name strings.
15857  * @constructor
15858  * @param {Object} config
15859  */
15860 Roo.data.JsonStore = function(c){
15861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863         reader: new Roo.data.JsonReader(c, c.fields)
15864     }));
15865 };
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877  
15878 Roo.data.Field = function(config){
15879     if(typeof config == "string"){
15880         config = {name: config};
15881     }
15882     Roo.apply(this, config);
15883     
15884     if(!this.type){
15885         this.type = "auto";
15886     }
15887     
15888     var st = Roo.data.SortTypes;
15889     // named sortTypes are supported, here we look them up
15890     if(typeof this.sortType == "string"){
15891         this.sortType = st[this.sortType];
15892     }
15893     
15894     // set default sortType for strings and dates
15895     if(!this.sortType){
15896         switch(this.type){
15897             case "string":
15898                 this.sortType = st.asUCString;
15899                 break;
15900             case "date":
15901                 this.sortType = st.asDate;
15902                 break;
15903             default:
15904                 this.sortType = st.none;
15905         }
15906     }
15907
15908     // define once
15909     var stripRe = /[\$,%]/g;
15910
15911     // prebuilt conversion function for this field, instead of
15912     // switching every time we're reading a value
15913     if(!this.convert){
15914         var cv, dateFormat = this.dateFormat;
15915         switch(this.type){
15916             case "":
15917             case "auto":
15918             case undefined:
15919                 cv = function(v){ return v; };
15920                 break;
15921             case "string":
15922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923                 break;
15924             case "int":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15928                     };
15929                 break;
15930             case "float":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15934                     };
15935                 break;
15936             case "bool":
15937             case "boolean":
15938                 cv = function(v){ return v === true || v === "true" || v == 1; };
15939                 break;
15940             case "date":
15941                 cv = function(v){
15942                     if(!v){
15943                         return '';
15944                     }
15945                     if(v instanceof Date){
15946                         return v;
15947                     }
15948                     if(dateFormat){
15949                         if(dateFormat == "timestamp"){
15950                             return new Date(v*1000);
15951                         }
15952                         return Date.parseDate(v, dateFormat);
15953                     }
15954                     var parsed = Date.parse(v);
15955                     return parsed ? new Date(parsed) : null;
15956                 };
15957              break;
15958             
15959         }
15960         this.convert = cv;
15961     }
15962 };
15963
15964 Roo.data.Field.prototype = {
15965     dateFormat: null,
15966     defaultValue: "",
15967     mapping: null,
15968     sortType : null,
15969     sortDir : "ASC"
15970 };/*
15971  * Based on:
15972  * Ext JS Library 1.1.1
15973  * Copyright(c) 2006-2007, Ext JS, LLC.
15974  *
15975  * Originally Released Under LGPL - original licence link has changed is not relivant.
15976  *
15977  * Fork - LGPL
15978  * <script type="text/javascript">
15979  */
15980  
15981 // Base class for reading structured data from a data source.  This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15983
15984 /**
15985  * @class Roo.data.DataReader
15986  * @abstract
15987  * Base class for reading structured data from a data source.  This class is intended to be
15988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15989  */
15990
15991 Roo.data.DataReader = function(meta, recordType){
15992     
15993     this.meta = meta;
15994     
15995     this.recordType = recordType instanceof Array ? 
15996         Roo.data.Record.create(recordType) : recordType;
15997 };
15998
15999 Roo.data.DataReader.prototype = {
16000     
16001     
16002     readerType : 'Data',
16003      /**
16004      * Create an empty record
16005      * @param {Object} data (optional) - overlay some values
16006      * @return {Roo.data.Record} record created.
16007      */
16008     newRow :  function(d) {
16009         var da =  {};
16010         this.recordType.prototype.fields.each(function(c) {
16011             switch( c.type) {
16012                 case 'int' : da[c.name] = 0; break;
16013                 case 'date' : da[c.name] = new Date(); break;
16014                 case 'float' : da[c.name] = 0.0; break;
16015                 case 'boolean' : da[c.name] = false; break;
16016                 default : da[c.name] = ""; break;
16017             }
16018             
16019         });
16020         return new this.recordType(Roo.apply(da, d));
16021     }
16022     
16023     
16024 };/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.DataProxy
16037  * @extends Roo.util.Observable
16038  * @abstract
16039  * This class is an abstract base class for implementations which provide retrieval of
16040  * unformatted data objects.<br>
16041  * <p>
16042  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043  * (of the appropriate type which knows how to parse the data object) to provide a block of
16044  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045  * <p>
16046  * Custom implementations must implement the load method as described in
16047  * {@link Roo.data.HttpProxy#load}.
16048  */
16049 Roo.data.DataProxy = function(){
16050     this.addEvents({
16051         /**
16052          * @event beforeload
16053          * Fires before a network request is made to retrieve a data object.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} params The params parameter to the load function.
16056          */
16057         beforeload : true,
16058         /**
16059          * @event load
16060          * Fires before the load method's callback is called.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} o The data object.
16063          * @param {Object} arg The callback argument object passed to the load function.
16064          */
16065         load : true,
16066         /**
16067          * @event loadexception
16068          * Fires if an Exception occurs during data retrieval.
16069          * @param {Object} This DataProxy object.
16070          * @param {Object} o The data object.
16071          * @param {Object} arg The callback argument object passed to the load function.
16072          * @param {Object} e The Exception.
16073          */
16074         loadexception : true
16075     });
16076     Roo.data.DataProxy.superclass.constructor.call(this);
16077 };
16078
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16080
16081     /**
16082      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083      */
16084 /*
16085  * Based on:
16086  * Ext JS Library 1.1.1
16087  * Copyright(c) 2006-2007, Ext JS, LLC.
16088  *
16089  * Originally Released Under LGPL - original licence link has changed is not relivant.
16090  *
16091  * Fork - LGPL
16092  * <script type="text/javascript">
16093  */
16094 /**
16095  * @class Roo.data.MemoryProxy
16096  * @extends Roo.data.DataProxy
16097  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098  * to the Reader when its load method is called.
16099  * @constructor
16100  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16101  */
16102 Roo.data.MemoryProxy = function(config){
16103     var data = config;
16104     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105         data = config.data;
16106     }
16107     Roo.data.MemoryProxy.superclass.constructor.call(this);
16108     this.data = data;
16109 };
16110
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16112     
16113     /**
16114      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16115      */
16116     /**
16117      * Load data from the requested source (in this case an in-memory
16118      * data object passed to the constructor), read the data object into
16119      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120      * process that block using the passed callback.
16121      * @param {Object} params This parameter is not used by the MemoryProxy class.
16122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123      * object into a block of Roo.data.Records.
16124      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125      * The function must be passed <ul>
16126      * <li>The Record block object</li>
16127      * <li>The "arg" argument from the load function</li>
16128      * <li>A boolean success indicator</li>
16129      * </ul>
16130      * @param {Object} scope The scope in which to call the callback
16131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132      */
16133     load : function(params, reader, callback, scope, arg){
16134         params = params || {};
16135         var result;
16136         try {
16137             result = reader.readRecords(params.data ? params.data :this.data);
16138         }catch(e){
16139             this.fireEvent("loadexception", this, arg, null, e);
16140             callback.call(scope, null, arg, false);
16141             return;
16142         }
16143         callback.call(scope, result, arg, true);
16144     },
16145     
16146     // private
16147     update : function(params, records){
16148         
16149     }
16150 });/*
16151  * Based on:
16152  * Ext JS Library 1.1.1
16153  * Copyright(c) 2006-2007, Ext JS, LLC.
16154  *
16155  * Originally Released Under LGPL - original licence link has changed is not relivant.
16156  *
16157  * Fork - LGPL
16158  * <script type="text/javascript">
16159  */
16160 /**
16161  * @class Roo.data.HttpProxy
16162  * @extends Roo.data.DataProxy
16163  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164  * configured to reference a certain URL.<br><br>
16165  * <p>
16166  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167  * from which the running page was served.<br><br>
16168  * <p>
16169  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170  * <p>
16171  * Be aware that to enable the browser to parse an XML document, the server must set
16172  * the Content-Type header in the HTTP response to "text/xml".
16173  * @constructor
16174  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176  * will be used to make the request.
16177  */
16178 Roo.data.HttpProxy = function(conn){
16179     Roo.data.HttpProxy.superclass.constructor.call(this);
16180     // is conn a conn config or a real conn?
16181     this.conn = conn;
16182     this.useAjax = !conn || !conn.events;
16183   
16184 };
16185
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187     // thse are take from connection...
16188     
16189     /**
16190      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16191      */
16192     /**
16193      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16194      * extra parameters to each request made by this object. (defaults to undefined)
16195      */
16196     /**
16197      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16198      *  to each request made by this object. (defaults to undefined)
16199      */
16200     /**
16201      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16202      */
16203     /**
16204      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16205      */
16206      /**
16207      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16208      * @type Boolean
16209      */
16210   
16211
16212     /**
16213      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214      * @type Boolean
16215      */
16216     /**
16217      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219      * a finer-grained basis than the DataProxy events.
16220      */
16221     getConnection : function(){
16222         return this.useAjax ? Roo.Ajax : this.conn;
16223     },
16224
16225     /**
16226      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228      * process that block using the passed callback.
16229      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230      * for the request to the remote server.
16231      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232      * object into a block of Roo.data.Records.
16233      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234      * The function must be passed <ul>
16235      * <li>The Record block object</li>
16236      * <li>The "arg" argument from the load function</li>
16237      * <li>A boolean success indicator</li>
16238      * </ul>
16239      * @param {Object} scope The scope in which to call the callback
16240      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241      */
16242     load : function(params, reader, callback, scope, arg){
16243         if(this.fireEvent("beforeload", this, params) !== false){
16244             var  o = {
16245                 params : params || {},
16246                 request: {
16247                     callback : callback,
16248                     scope : scope,
16249                     arg : arg
16250                 },
16251                 reader: reader,
16252                 callback : this.loadResponse,
16253                 scope: this
16254             };
16255             if(this.useAjax){
16256                 Roo.applyIf(o, this.conn);
16257                 if(this.activeRequest){
16258                     Roo.Ajax.abort(this.activeRequest);
16259                 }
16260                 this.activeRequest = Roo.Ajax.request(o);
16261             }else{
16262                 this.conn.request(o);
16263             }
16264         }else{
16265             callback.call(scope||this, null, arg, false);
16266         }
16267     },
16268
16269     // private
16270     loadResponse : function(o, success, response){
16271         delete this.activeRequest;
16272         if(!success){
16273             this.fireEvent("loadexception", this, o, response);
16274             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16275             return;
16276         }
16277         var result;
16278         try {
16279             result = o.reader.read(response);
16280         }catch(e){
16281             o.success = false;
16282             o.raw = { errorMsg : response.responseText };
16283             this.fireEvent("loadexception", this, o, response, e);
16284             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285             return;
16286         }
16287         
16288         this.fireEvent("load", this, o, o.request.arg);
16289         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290     },
16291
16292     // private
16293     update : function(dataSet){
16294
16295     },
16296
16297     // private
16298     updateResponse : function(dataSet){
16299
16300     }
16301 });/*
16302  * Based on:
16303  * Ext JS Library 1.1.1
16304  * Copyright(c) 2006-2007, Ext JS, LLC.
16305  *
16306  * Originally Released Under LGPL - original licence link has changed is not relivant.
16307  *
16308  * Fork - LGPL
16309  * <script type="text/javascript">
16310  */
16311
16312 /**
16313  * @class Roo.data.ScriptTagProxy
16314  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315  * other than the originating domain of the running page.<br><br>
16316  * <p>
16317  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16318  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319  * <p>
16320  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321  * source code that is used as the source inside a &lt;script> tag.<br><br>
16322  * <p>
16323  * In order for the browser to process the returned data, the server must wrap the data object
16324  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326  * depending on whether the callback name was passed:
16327  * <p>
16328  * <pre><code>
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16331 if (cb != null) {
16332     scriptTag = true;
16333     response.setContentType("text/javascript");
16334 } else {
16335     response.setContentType("application/x-json");
16336 }
16337 Writer out = response.getWriter();
16338 if (scriptTag) {
16339     out.write(cb + "(");
16340 }
16341 out.print(dataBlock.toJsonString());
16342 if (scriptTag) {
16343     out.write(");");
16344 }
16345 </pre></code>
16346  *
16347  * @constructor
16348  * @param {Object} config A configuration object.
16349  */
16350 Roo.data.ScriptTagProxy = function(config){
16351     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352     Roo.apply(this, config);
16353     this.head = document.getElementsByTagName("head")[0];
16354 };
16355
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359     /**
16360      * @cfg {String} url The URL from which to request the data object.
16361      */
16362     /**
16363      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364      */
16365     timeout : 30000,
16366     /**
16367      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368      * the server the name of the callback function set up by the load call to process the returned data object.
16369      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370      * javascript output which calls this named function passing the data object as its only parameter.
16371      */
16372     callbackParam : "callback",
16373     /**
16374      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375      * name to the request.
16376      */
16377     nocache : true,
16378
16379     /**
16380      * Load data from the configured URL, read the data object into
16381      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382      * process that block using the passed callback.
16383      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384      * for the request to the remote server.
16385      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386      * object into a block of Roo.data.Records.
16387      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388      * The function must be passed <ul>
16389      * <li>The Record block object</li>
16390      * <li>The "arg" argument from the load function</li>
16391      * <li>A boolean success indicator</li>
16392      * </ul>
16393      * @param {Object} scope The scope in which to call the callback
16394      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395      */
16396     load : function(params, reader, callback, scope, arg){
16397         if(this.fireEvent("beforeload", this, params) !== false){
16398
16399             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400
16401             var url = this.url;
16402             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403             if(this.nocache){
16404                 url += "&_dc=" + (new Date().getTime());
16405             }
16406             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16407             var trans = {
16408                 id : transId,
16409                 cb : "stcCallback"+transId,
16410                 scriptId : "stcScript"+transId,
16411                 params : params,
16412                 arg : arg,
16413                 url : url,
16414                 callback : callback,
16415                 scope : scope,
16416                 reader : reader
16417             };
16418             var conn = this;
16419
16420             window[trans.cb] = function(o){
16421                 conn.handleResponse(o, trans);
16422             };
16423
16424             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425
16426             if(this.autoAbort !== false){
16427                 this.abort();
16428             }
16429
16430             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431
16432             var script = document.createElement("script");
16433             script.setAttribute("src", url);
16434             script.setAttribute("type", "text/javascript");
16435             script.setAttribute("id", trans.scriptId);
16436             this.head.appendChild(script);
16437
16438             this.trans = trans;
16439         }else{
16440             callback.call(scope||this, null, arg, false);
16441         }
16442     },
16443
16444     // private
16445     isLoading : function(){
16446         return this.trans ? true : false;
16447     },
16448
16449     /**
16450      * Abort the current server request.
16451      */
16452     abort : function(){
16453         if(this.isLoading()){
16454             this.destroyTrans(this.trans);
16455         }
16456     },
16457
16458     // private
16459     destroyTrans : function(trans, isLoaded){
16460         this.head.removeChild(document.getElementById(trans.scriptId));
16461         clearTimeout(trans.timeoutId);
16462         if(isLoaded){
16463             window[trans.cb] = undefined;
16464             try{
16465                 delete window[trans.cb];
16466             }catch(e){}
16467         }else{
16468             // if hasn't been loaded, wait for load to remove it to prevent script error
16469             window[trans.cb] = function(){
16470                 window[trans.cb] = undefined;
16471                 try{
16472                     delete window[trans.cb];
16473                 }catch(e){}
16474             };
16475         }
16476     },
16477
16478     // private
16479     handleResponse : function(o, trans){
16480         this.trans = false;
16481         this.destroyTrans(trans, true);
16482         var result;
16483         try {
16484             result = trans.reader.readRecords(o);
16485         }catch(e){
16486             this.fireEvent("loadexception", this, o, trans.arg, e);
16487             trans.callback.call(trans.scope||window, null, trans.arg, false);
16488             return;
16489         }
16490         this.fireEvent("load", this, o, trans.arg);
16491         trans.callback.call(trans.scope||window, result, trans.arg, true);
16492     },
16493
16494     // private
16495     handleFailure : function(trans){
16496         this.trans = false;
16497         this.destroyTrans(trans, false);
16498         this.fireEvent("loadexception", this, null, trans.arg);
16499         trans.callback.call(trans.scope||window, null, trans.arg, false);
16500     }
16501 });/*
16502  * Based on:
16503  * Ext JS Library 1.1.1
16504  * Copyright(c) 2006-2007, Ext JS, LLC.
16505  *
16506  * Originally Released Under LGPL - original licence link has changed is not relivant.
16507  *
16508  * Fork - LGPL
16509  * <script type="text/javascript">
16510  */
16511
16512 /**
16513  * @class Roo.data.JsonReader
16514  * @extends Roo.data.DataReader
16515  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516  * based on mappings in a provided Roo.data.Record constructor.
16517  * 
16518  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519  * in the reply previously. 
16520  * 
16521  * <p>
16522  * Example code:
16523  * <pre><code>
16524 var RecordDef = Roo.data.Record.create([
16525     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16526     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16527 ]);
16528 var myReader = new Roo.data.JsonReader({
16529     totalProperty: "results",    // The property which contains the total dataset size (optional)
16530     root: "rows",                // The property which contains an Array of row objects
16531     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16532 }, RecordDef);
16533 </code></pre>
16534  * <p>
16535  * This would consume a JSON file like this:
16536  * <pre><code>
16537 { 'results': 2, 'rows': [
16538     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16540 }
16541 </code></pre>
16542  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544  * paged from the remote server.
16545  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546  * @cfg {String} root name of the property which contains the Array of row objects.
16547  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548  * @cfg {Array} fields Array of field definition objects
16549  * @constructor
16550  * Create a new JsonReader
16551  * @param {Object} meta Metadata configuration options
16552  * @param {Object} recordType Either an Array of field definition objects,
16553  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554  */
16555 Roo.data.JsonReader = function(meta, recordType){
16556     
16557     meta = meta || {};
16558     // set some defaults:
16559     Roo.applyIf(meta, {
16560         totalProperty: 'total',
16561         successProperty : 'success',
16562         root : 'data',
16563         id : 'id'
16564     });
16565     
16566     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 };
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569     
16570     readerType : 'Json',
16571     
16572     /**
16573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16574      * Used by Store query builder to append _requestMeta to params.
16575      * 
16576      */
16577     metaFromRemote : false,
16578     /**
16579      * This method is only used by a DataProxy which has retrieved data from a remote server.
16580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581      * @return {Object} data A data block which is used by an Roo.data.Store object as
16582      * a cache of Roo.data.Records.
16583      */
16584     read : function(response){
16585         var json = response.responseText;
16586        
16587         var o = /* eval:var:o */ eval("("+json+")");
16588         if(!o) {
16589             throw {message: "JsonReader.read: Json object not found"};
16590         }
16591         
16592         if(o.metaData){
16593             
16594             delete this.ef;
16595             this.metaFromRemote = true;
16596             this.meta = o.metaData;
16597             this.recordType = Roo.data.Record.create(o.metaData.fields);
16598             this.onMetaChange(this.meta, this.recordType, o);
16599         }
16600         return this.readRecords(o);
16601     },
16602
16603     // private function a store will implement
16604     onMetaChange : function(meta, recordType, o){
16605
16606     },
16607
16608     /**
16609          * @ignore
16610          */
16611     simpleAccess: function(obj, subsc) {
16612         return obj[subsc];
16613     },
16614
16615         /**
16616          * @ignore
16617          */
16618     getJsonAccessor: function(){
16619         var re = /[\[\.]/;
16620         return function(expr) {
16621             try {
16622                 return(re.test(expr))
16623                     ? new Function("obj", "return obj." + expr)
16624                     : function(obj){
16625                         return obj[expr];
16626                     };
16627             } catch(e){}
16628             return Roo.emptyFn;
16629         };
16630     }(),
16631
16632     /**
16633      * Create a data block containing Roo.data.Records from an XML document.
16634      * @param {Object} o An object which contains an Array of row objects in the property specified
16635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636      * which contains the total size of the dataset.
16637      * @return {Object} data A data block which is used by an Roo.data.Store object as
16638      * a cache of Roo.data.Records.
16639      */
16640     readRecords : function(o){
16641         /**
16642          * After any data loads, the raw JSON data is available for further custom processing.
16643          * @type Object
16644          */
16645         this.o = o;
16646         var s = this.meta, Record = this.recordType,
16647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648
16649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16650         if (!this.ef) {
16651             if(s.totalProperty) {
16652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16653                 }
16654                 if(s.successProperty) {
16655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16656                 }
16657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658                 if (s.id) {
16659                         var g = this.getJsonAccessor(s.id);
16660                         this.getId = function(rec) {
16661                                 var r = g(rec);  
16662                                 return (r === undefined || r === "") ? null : r;
16663                         };
16664                 } else {
16665                         this.getId = function(){return null;};
16666                 }
16667             this.ef = [];
16668             for(var jj = 0; jj < fl; jj++){
16669                 f = fi[jj];
16670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671                 this.ef[jj] = this.getJsonAccessor(map);
16672             }
16673         }
16674
16675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676         if(s.totalProperty){
16677             var vt = parseInt(this.getTotal(o), 10);
16678             if(!isNaN(vt)){
16679                 totalRecords = vt;
16680             }
16681         }
16682         if(s.successProperty){
16683             var vs = this.getSuccess(o);
16684             if(vs === false || vs === 'false'){
16685                 success = false;
16686             }
16687         }
16688         var records = [];
16689         for(var i = 0; i < c; i++){
16690             var n = root[i];
16691             var values = {};
16692             var id = this.getId(n);
16693             for(var j = 0; j < fl; j++){
16694                 f = fi[j];
16695                                 var v = this.ef[j](n);
16696                                 if (!f.convert) {
16697                                         Roo.log('missing convert for ' + f.name);
16698                                         Roo.log(f);
16699                                         continue;
16700                                 }
16701                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702             }
16703                         if (!Record) {
16704                                 return {
16705                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706                                         success : false,
16707                                         records : [],
16708                                         totalRecords : 0
16709                                 };
16710                         }
16711             var record = new Record(values, id);
16712             record.json = n;
16713             records[i] = record;
16714         }
16715         return {
16716             raw : o,
16717             success : success,
16718             records : records,
16719             totalRecords : totalRecords
16720         };
16721     },
16722     // used when loading children.. @see loadDataFromChildren
16723     toLoadData: function(rec)
16724     {
16725         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727         return { data : data, total : data.length };
16728         
16729     }
16730 });/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.data.ArrayReader
16743  * @extends Roo.data.DataReader
16744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745  * Each element of that Array represents a row of data fields. The
16746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748  * <p>
16749  * Example code:.
16750  * <pre><code>
16751 var RecordDef = Roo.data.Record.create([
16752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16754 ]);
16755 var myReader = new Roo.data.ArrayReader({
16756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16757 }, RecordDef);
16758 </code></pre>
16759  * <p>
16760  * This would consume an Array like this:
16761  * <pre><code>
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763   </code></pre>
16764  
16765  * @constructor
16766  * Create a new JsonReader
16767  * @param {Object} meta Metadata configuration options.
16768  * @param {Object|Array} recordType Either an Array of field definition objects
16769  * 
16770  * @cfg {Array} fields Array of field definition objects
16771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772  * as specified to {@link Roo.data.Record#create},
16773  * or an {@link Roo.data.Record} object
16774  *
16775  * 
16776  * created using {@link Roo.data.Record#create}.
16777  */
16778 Roo.data.ArrayReader = function(meta, recordType)
16779 {    
16780     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16781 };
16782
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16784     
16785       /**
16786      * Create a data block containing Roo.data.Records from an XML document.
16787      * @param {Object} o An Array of row objects which represents the dataset.
16788      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789      * a cache of Roo.data.Records.
16790      */
16791     readRecords : function(o)
16792     {
16793         var sid = this.meta ? this.meta.id : null;
16794         var recordType = this.recordType, fields = recordType.prototype.fields;
16795         var records = [];
16796         var root = o;
16797         for(var i = 0; i < root.length; i++){
16798             var n = root[i];
16799             var values = {};
16800             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801             for(var j = 0, jlen = fields.length; j < jlen; j++){
16802                 var f = fields.items[j];
16803                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805                 v = f.convert(v);
16806                 values[f.name] = v;
16807             }
16808             var record = new recordType(values, id);
16809             record.json = n;
16810             records[records.length] = record;
16811         }
16812         return {
16813             records : records,
16814             totalRecords : records.length
16815         };
16816     },
16817     // used when loading children.. @see loadDataFromChildren
16818     toLoadData: function(rec)
16819     {
16820         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822         
16823     }
16824     
16825     
16826 });/*
16827  * - LGPL
16828  * * 
16829  */
16830
16831 /**
16832  * @class Roo.bootstrap.form.ComboBox
16833  * @extends Roo.bootstrap.form.TriggerField
16834  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835  * @cfg {Boolean} append (true|false) default false
16836  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841  * @cfg {Boolean} animate default true
16842  * @cfg {Boolean} emptyResultText only for touch device
16843  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844  * @cfg {String} emptyTitle default ''
16845  * @cfg {Number} width fixed with? experimental
16846  * @constructor
16847  * Create a new ComboBox.
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.bootstrap.form.ComboBox = function(config){
16851     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event expand
16855          * Fires when the dropdown list is expanded
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'expand' : true,
16859         /**
16860          * @event collapse
16861          * Fires when the dropdown list is collapsed
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'collapse' : true,
16865         /**
16866          * @event beforeselect
16867          * Fires before a list item is selected. Return false to cancel the selection.
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'beforeselect' : true,
16873         /**
16874          * @event select
16875          * Fires when a list item is selected
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878         * @param {Number} index The index of the selected item in the dropdown list
16879         */
16880         'select' : true,
16881         /**
16882          * @event beforequery
16883          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884          * The event object passed has these properties:
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         * @param {String} query The query
16887         * @param {Boolean} forceAll true to force "all" query
16888         * @param {Boolean} cancel true to cancel the query
16889         * @param {Object} e The query event object
16890         */
16891         'beforequery': true,
16892          /**
16893          * @event add
16894          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'add' : true,
16898         /**
16899          * @event edit
16900          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16903         */
16904         'edit' : true,
16905         /**
16906          * @event remove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'remove' : true,
16911         /**
16912          * @event afterremove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'afterremove' : true,
16917         /**
16918          * @event specialfilter
16919          * Fires when specialfilter
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'specialfilter' : true,
16923         /**
16924          * @event tick
16925          * Fires when tick the element
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'tick' : true,
16929         /**
16930          * @event touchviewdisplay
16931          * Fires when touch view require special display (default is using displayField)
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             * @param {Object} cfg set html .
16934             */
16935         'touchviewdisplay' : true
16936         
16937     });
16938     
16939     this.item = [];
16940     this.tickItems = [];
16941     
16942     this.selectedIndex = -1;
16943     if(this.mode == 'local'){
16944         if(config.queryDelay === undefined){
16945             this.queryDelay = 10;
16946         }
16947         if(config.minChars === undefined){
16948             this.minChars = 0;
16949         }
16950     }
16951 };
16952
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16954      
16955     /**
16956      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957      * rendering into an Roo.Editor, defaults to false)
16958      */
16959     /**
16960      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16962      */
16963     /**
16964      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16965      */
16966     /**
16967      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968      * the dropdown list (defaults to undefined, with no header element)
16969      */
16970
16971      /**
16972      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16973      */
16974      
16975      /**
16976      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977      */
16978     listWidth: undefined,
16979     /**
16980      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'text' if mode = 'local')
16982      */
16983     displayField: undefined,
16984     
16985     /**
16986      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'value' if mode = 'local'). 
16988      * Note: use of a valueField requires the user make a selection
16989      * in order for a value to be mapped.
16990      */
16991     valueField: undefined,
16992     /**
16993      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16994      */
16995     modalTitle : '',
16996     
16997     /**
16998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999      * field's data value (defaults to the underlying DOM element's name)
17000      */
17001     hiddenName: undefined,
17002     /**
17003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004      */
17005     listClass: '',
17006     /**
17007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008      */
17009     selectedClass: 'active',
17010     
17011     /**
17012      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013      */
17014     shadow:'sides',
17015     /**
17016      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017      * anchor positions (defaults to 'tl-bl')
17018      */
17019     listAlign: 'tl-bl?',
17020     /**
17021      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022      */
17023     maxHeight: 300,
17024     /**
17025      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17026      * query specified by the allQuery config option (defaults to 'query')
17027      */
17028     triggerAction: 'query',
17029     /**
17030      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031      * (defaults to 4, does not apply if editable = false)
17032      */
17033     minChars : 4,
17034     /**
17035      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037      */
17038     typeAhead: false,
17039     /**
17040      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042      */
17043     queryDelay: 500,
17044     /**
17045      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17047      */
17048     pageSize: 0,
17049     /**
17050      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17051      * when editable = true (defaults to false)
17052      */
17053     selectOnFocus:false,
17054     /**
17055      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056      */
17057     queryParam: 'query',
17058     /**
17059      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17060      * when mode = 'remote' (defaults to 'Loading...')
17061      */
17062     loadingText: 'Loading...',
17063     /**
17064      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065      */
17066     resizable: false,
17067     /**
17068      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069      */
17070     handleHeight : 8,
17071     /**
17072      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073      * traditional select (defaults to true)
17074      */
17075     editable: true,
17076     /**
17077      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078      */
17079     allQuery: '',
17080     /**
17081      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082      */
17083     mode: 'remote',
17084     /**
17085      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086      * listWidth has a higher value)
17087      */
17088     minListWidth : 70,
17089     /**
17090      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091      * allow the user to set arbitrary text into the field (defaults to false)
17092      */
17093     forceSelection:false,
17094     /**
17095      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096      * if typeAhead = true (defaults to 250)
17097      */
17098     typeAheadDelay : 250,
17099     /**
17100      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102      */
17103     valueNotFoundText : undefined,
17104     /**
17105      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106      */
17107     blockFocus : false,
17108     
17109     /**
17110      * @cfg {Boolean} disableClear Disable showing of clear button.
17111      */
17112     disableClear : false,
17113     /**
17114      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17115      */
17116     alwaysQuery : false,
17117     
17118     /**
17119      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17120      */
17121     multiple : false,
17122     
17123     /**
17124      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     invalidClass : "has-warning",
17127     
17128     /**
17129      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130      */
17131     validClass : "has-success",
17132     
17133     /**
17134      * @cfg {Boolean} specialFilter (true|false) special filter default false
17135      */
17136     specialFilter : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140      */
17141     mobileTouchView : true,
17142     
17143     /**
17144      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145      */
17146     useNativeIOS : false,
17147     
17148     /**
17149      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150      */
17151     mobile_restrict_height : false,
17152     
17153     ios_options : false,
17154     
17155     //private
17156     addicon : false,
17157     editicon: false,
17158     
17159     page: 0,
17160     hasQuery: false,
17161     append: false,
17162     loadNext: false,
17163     autoFocus : true,
17164     tickable : false,
17165     btnPosition : 'right',
17166     triggerList : true,
17167     showToggleBtn : true,
17168     animate : true,
17169     emptyResultText: 'Empty',
17170     triggerText : 'Select',
17171     emptyTitle : '',
17172     width : false,
17173     
17174     // element that contains real text value.. (when hidden is used..)
17175     
17176     getAutoCreate : function()
17177     {   
17178         var cfg = false;
17179         //render
17180         /*
17181          * Render classic select for iso
17182          */
17183         
17184         if(Roo.isIOS && this.useNativeIOS){
17185             cfg = this.getAutoCreateNativeIOS();
17186             return cfg;
17187         }
17188         
17189         /*
17190          * Touch Devices
17191          */
17192         
17193         if(Roo.isTouch && this.mobileTouchView){
17194             cfg = this.getAutoCreateTouchView();
17195             return cfg;;
17196         }
17197         
17198         /*
17199          *  Normal ComboBox
17200          */
17201         if(!this.tickable){
17202             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17203             return cfg;
17204         }
17205         
17206         /*
17207          *  ComboBox with tickable selections
17208          */
17209              
17210         var align = this.labelAlign || this.parentLabelAlign();
17211         
17212         cfg = {
17213             cls : 'form-group roo-combobox-tickable' //input-group
17214         };
17215         
17216         var btn_text_select = '';
17217         var btn_text_done = '';
17218         var btn_text_cancel = '';
17219         
17220         if (this.btn_text_show) {
17221             btn_text_select = 'Select';
17222             btn_text_done = 'Done';
17223             btn_text_cancel = 'Cancel'; 
17224         }
17225         
17226         var buttons = {
17227             tag : 'div',
17228             cls : 'tickable-buttons',
17229             cn : [
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234                     //html : this.triggerText
17235                     html: btn_text_select
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'ok',
17241                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242                     //html : 'Done'
17243                     html: btn_text_done
17244                 },
17245                 {
17246                     tag : 'button',
17247                     type : 'button',
17248                     name : 'cancel',
17249                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250                     //html : 'Cancel'
17251                     html: btn_text_cancel
17252                 }
17253             ]
17254         };
17255         
17256         if(this.editable){
17257             buttons.cn.unshift({
17258                 tag: 'input',
17259                 cls: 'roo-select2-search-field-input'
17260             });
17261         }
17262         
17263         var _this = this;
17264         
17265         Roo.each(buttons.cn, function(c){
17266             if (_this.size) {
17267                 c.cls += ' btn-' + _this.size;
17268             }
17269
17270             if (_this.disabled) {
17271                 c.disabled = true;
17272             }
17273         });
17274         
17275         var box = {
17276             tag: 'div',
17277             style : 'display: contents',
17278             cn: [
17279                 {
17280                     tag: 'input',
17281                     type : 'hidden',
17282                     cls: 'form-hidden-field'
17283                 },
17284                 {
17285                     tag: 'ul',
17286                     cls: 'roo-select2-choices',
17287                     cn:[
17288                         {
17289                             tag: 'li',
17290                             cls: 'roo-select2-search-field',
17291                             cn: [
17292                                 buttons
17293                             ]
17294                         }
17295                     ]
17296                 }
17297             ]
17298         };
17299         
17300         var combobox = {
17301             cls: 'roo-select2-container input-group roo-select2-container-multi',
17302             cn: [
17303                 
17304                 box
17305 //                {
17306 //                    tag: 'ul',
17307 //                    cls: 'typeahead typeahead-long dropdown-menu',
17308 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17309 //                }
17310             ]
17311         };
17312         
17313         if(this.hasFeedback && !this.allowBlank){
17314             
17315             var feedback = {
17316                 tag: 'span',
17317                 cls: 'glyphicon form-control-feedback'
17318             };
17319
17320             combobox.cn.push(feedback);
17321         }
17322         
17323         
17324         
17325         var indicator = {
17326             tag : 'i',
17327             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328             tooltip : 'This field is required'
17329         };
17330         if (Roo.bootstrap.version == 4) {
17331             indicator = {
17332                 tag : 'i',
17333                 style : 'display:none'
17334             };
17335         }
17336         if (align ==='left' && this.fieldLabel.length) {
17337             
17338             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17339             
17340             cfg.cn = [
17341                 indicator,
17342                 {
17343                     tag: 'label',
17344                     'for' :  id,
17345                     cls : 'control-label col-form-label',
17346                     html : this.fieldLabel
17347
17348                 },
17349                 {
17350                     cls : "", 
17351                     cn: [
17352                         combobox
17353                     ]
17354                 }
17355
17356             ];
17357             
17358             var labelCfg = cfg.cn[1];
17359             var contentCfg = cfg.cn[2];
17360             
17361
17362             if(this.indicatorpos == 'right'){
17363                 
17364                 cfg.cn = [
17365                     {
17366                         tag: 'label',
17367                         'for' :  id,
17368                         cls : 'control-label col-form-label',
17369                         cn : [
17370                             {
17371                                 tag : 'span',
17372                                 html : this.fieldLabel
17373                             },
17374                             indicator
17375                         ]
17376                     },
17377                     {
17378                         cls : "",
17379                         cn: [
17380                             combobox
17381                         ]
17382                     }
17383
17384                 ];
17385                 
17386                 
17387                 
17388                 labelCfg = cfg.cn[0];
17389                 contentCfg = cfg.cn[1];
17390             
17391             }
17392             
17393             if(this.labelWidth > 12){
17394                 labelCfg.style = "width: " + this.labelWidth + 'px';
17395             }
17396             if(this.width * 1 > 0){
17397                 contentCfg.style = "width: " + this.width + 'px';
17398             }
17399             if(this.labelWidth < 13 && this.labelmd == 0){
17400                 this.labelmd = this.labelWidth;
17401             }
17402             
17403             if(this.labellg > 0){
17404                 labelCfg.cls += ' col-lg-' + this.labellg;
17405                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17406             }
17407             
17408             if(this.labelmd > 0){
17409                 labelCfg.cls += ' col-md-' + this.labelmd;
17410                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17411             }
17412             
17413             if(this.labelsm > 0){
17414                 labelCfg.cls += ' col-sm-' + this.labelsm;
17415                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17416             }
17417             
17418             if(this.labelxs > 0){
17419                 labelCfg.cls += ' col-xs-' + this.labelxs;
17420                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17421             }
17422                 
17423                 
17424         } else if ( this.fieldLabel.length) {
17425 //                Roo.log(" label");
17426                  cfg.cn = [
17427                    indicator,
17428                     {
17429                         tag: 'label',
17430                         //cls : 'input-group-addon',
17431                         html : this.fieldLabel
17432                     },
17433                     combobox
17434                 ];
17435                 
17436                 if(this.indicatorpos == 'right'){
17437                     cfg.cn = [
17438                         {
17439                             tag: 'label',
17440                             //cls : 'input-group-addon',
17441                             html : this.fieldLabel
17442                         },
17443                         indicator,
17444                         combobox
17445                     ];
17446                     
17447                 }
17448
17449         } else {
17450             
17451 //                Roo.log(" no label && no align");
17452                 cfg = combobox
17453                      
17454                 
17455         }
17456          
17457         var settings=this;
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         return cfg;
17465         
17466     },
17467     
17468     _initEventsCalled : false,
17469     
17470     // private
17471     initEvents: function()
17472     {   
17473         if (this._initEventsCalled) { // as we call render... prevent looping...
17474             return;
17475         }
17476         this._initEventsCalled = true;
17477         
17478         if (!this.store) {
17479             throw "can not find store for combo";
17480         }
17481         
17482         this.indicator = this.indicatorEl();
17483         
17484         this.store = Roo.factory(this.store, Roo.data);
17485         this.store.parent = this;
17486         
17487         // if we are building from html. then this element is so complex, that we can not really
17488         // use the rendered HTML.
17489         // so we have to trash and replace the previous code.
17490         if (Roo.XComponent.build_from_html) {
17491             // remove this element....
17492             var e = this.el.dom, k=0;
17493             while (e ) { e = e.previousSibling;  ++k;}
17494
17495             this.el.remove();
17496             
17497             this.el=false;
17498             this.rendered = false;
17499             
17500             this.render(this.parent().getChildContainer(true), k);
17501         }
17502         
17503         if(Roo.isIOS && this.useNativeIOS){
17504             this.initIOSView();
17505             return;
17506         }
17507         
17508         /*
17509          * Touch Devices
17510          */
17511         
17512         if(Roo.isTouch && this.mobileTouchView){
17513             this.initTouchView();
17514             return;
17515         }
17516         
17517         if(this.tickable){
17518             this.initTickableEvents();
17519             return;
17520         }
17521         
17522         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17523         
17524         if(this.hiddenName){
17525             
17526             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17527             
17528             this.hiddenField.dom.value =
17529                 this.hiddenValue !== undefined ? this.hiddenValue :
17530                 this.value !== undefined ? this.value : '';
17531
17532             // prevent input submission
17533             this.el.dom.removeAttribute('name');
17534             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17535              
17536              
17537         }
17538         //if(Roo.isGecko){
17539         //    this.el.dom.setAttribute('autocomplete', 'off');
17540         //}
17541         
17542         var cls = 'x-combo-list';
17543         
17544         //this.list = new Roo.Layer({
17545         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17546         //});
17547         
17548         var _this = this;
17549         
17550         (function(){
17551             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17552             _this.list.setWidth(lw);
17553         }).defer(100);
17554         
17555         this.list.on('mouseover', this.onViewOver, this);
17556         this.list.on('mousemove', this.onViewMove, this);
17557         this.list.on('scroll', this.onViewScroll, this);
17558         
17559         /*
17560         this.list.swallowEvent('mousewheel');
17561         this.assetHeight = 0;
17562
17563         if(this.title){
17564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17565             this.assetHeight += this.header.getHeight();
17566         }
17567
17568         this.innerList = this.list.createChild({cls:cls+'-inner'});
17569         this.innerList.on('mouseover', this.onViewOver, this);
17570         this.innerList.on('mousemove', this.onViewMove, this);
17571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17572         
17573         if(this.allowBlank && !this.pageSize && !this.disableClear){
17574             this.footer = this.list.createChild({cls:cls+'-ft'});
17575             this.pageTb = new Roo.Toolbar(this.footer);
17576            
17577         }
17578         if(this.pageSize){
17579             this.footer = this.list.createChild({cls:cls+'-ft'});
17580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17581                     {pageSize: this.pageSize});
17582             
17583         }
17584         
17585         if (this.pageTb && this.allowBlank && !this.disableClear) {
17586             var _this = this;
17587             this.pageTb.add(new Roo.Toolbar.Fill(), {
17588                 cls: 'x-btn-icon x-btn-clear',
17589                 text: '&#160;',
17590                 handler: function()
17591                 {
17592                     _this.collapse();
17593                     _this.clearValue();
17594                     _this.onSelect(false, -1);
17595                 }
17596             });
17597         }
17598         if (this.footer) {
17599             this.assetHeight += this.footer.getHeight();
17600         }
17601         */
17602             
17603         if(!this.tpl){
17604             this.tpl = Roo.bootstrap.version == 4 ?
17605                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17606                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17607         }
17608
17609         this.view = new Roo.View(this.list, this.tpl, {
17610             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17611         });
17612         //this.view.wrapEl.setDisplayed(false);
17613         this.view.on('click', this.onViewClick, this);
17614         
17615         
17616         this.store.on('beforeload', this.onBeforeLoad, this);
17617         this.store.on('load', this.onLoad, this);
17618         this.store.on('loadexception', this.onLoadException, this);
17619         /*
17620         if(this.resizable){
17621             this.resizer = new Roo.Resizable(this.list,  {
17622                pinned:true, handles:'se'
17623             });
17624             this.resizer.on('resize', function(r, w, h){
17625                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17626                 this.listWidth = w;
17627                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17628                 this.restrictHeight();
17629             }, this);
17630             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17631         }
17632         */
17633         if(!this.editable){
17634             this.editable = true;
17635             this.setEditable(false);
17636         }
17637         
17638         /*
17639         
17640         if (typeof(this.events.add.listeners) != 'undefined') {
17641             
17642             this.addicon = this.wrap.createChild(
17643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17644        
17645             this.addicon.on('click', function(e) {
17646                 this.fireEvent('add', this);
17647             }, this);
17648         }
17649         if (typeof(this.events.edit.listeners) != 'undefined') {
17650             
17651             this.editicon = this.wrap.createChild(
17652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17653             if (this.addicon) {
17654                 this.editicon.setStyle('margin-left', '40px');
17655             }
17656             this.editicon.on('click', function(e) {
17657                 
17658                 // we fire even  if inothing is selected..
17659                 this.fireEvent('edit', this, this.lastData );
17660                 
17661             }, this);
17662         }
17663         */
17664         
17665         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17666             "up" : function(e){
17667                 this.inKeyMode = true;
17668                 this.selectPrev();
17669             },
17670
17671             "down" : function(e){
17672                 if(!this.isExpanded()){
17673                     this.onTriggerClick();
17674                 }else{
17675                     this.inKeyMode = true;
17676                     this.selectNext();
17677                 }
17678             },
17679
17680             "enter" : function(e){
17681 //                this.onViewClick();
17682                 //return true;
17683                 this.collapse();
17684                 
17685                 if(this.fireEvent("specialkey", this, e)){
17686                     this.onViewClick(false);
17687                 }
17688                 
17689                 return true;
17690             },
17691
17692             "esc" : function(e){
17693                 this.collapse();
17694             },
17695
17696             "tab" : function(e){
17697                 this.collapse();
17698                 
17699                 if(this.fireEvent("specialkey", this, e)){
17700                     this.onViewClick(false);
17701                 }
17702                 
17703                 return true;
17704             },
17705
17706             scope : this,
17707
17708             doRelay : function(foo, bar, hname){
17709                 if(hname == 'down' || this.scope.isExpanded()){
17710                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17711                 }
17712                 return true;
17713             },
17714
17715             forceKeyDown: true
17716         });
17717         
17718         
17719         this.queryDelay = Math.max(this.queryDelay || 10,
17720                 this.mode == 'local' ? 10 : 250);
17721         
17722         
17723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17724         
17725         if(this.typeAhead){
17726             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17727         }
17728         if(this.editable !== false){
17729             this.inputEl().on("keyup", this.onKeyUp, this);
17730         }
17731         if(this.forceSelection){
17732             this.inputEl().on('blur', this.doForce, this);
17733         }
17734         
17735         if(this.multiple){
17736             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17737             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17738         }
17739     },
17740     
17741     initTickableEvents: function()
17742     {   
17743         this.createList();
17744         
17745         if(this.hiddenName){
17746             
17747             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748             
17749             this.hiddenField.dom.value =
17750                 this.hiddenValue !== undefined ? this.hiddenValue :
17751                 this.value !== undefined ? this.value : '';
17752
17753             // prevent input submission
17754             this.el.dom.removeAttribute('name');
17755             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17756              
17757              
17758         }
17759         
17760 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17761         
17762         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17763         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17764         if(this.triggerList){
17765             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17766         }
17767          
17768         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17769         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17770         
17771         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17772         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17773         
17774         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17775         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17776         
17777         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780         
17781         this.okBtn.hide();
17782         this.cancelBtn.hide();
17783         
17784         var _this = this;
17785         
17786         (function(){
17787             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17788             _this.list.setWidth(lw);
17789         }).defer(100);
17790         
17791         this.list.on('mouseover', this.onViewOver, this);
17792         this.list.on('mousemove', this.onViewMove, this);
17793         
17794         this.list.on('scroll', this.onViewScroll, this);
17795         
17796         if(!this.tpl){
17797             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17798                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17799         }
17800
17801         this.view = new Roo.View(this.list, this.tpl, {
17802             singleSelect:true,
17803             tickable:true,
17804             parent:this,
17805             store: this.store,
17806             selectedClass: this.selectedClass
17807         });
17808         
17809         //this.view.wrapEl.setDisplayed(false);
17810         this.view.on('click', this.onViewClick, this);
17811         
17812         
17813         
17814         this.store.on('beforeload', this.onBeforeLoad, this);
17815         this.store.on('load', this.onLoad, this);
17816         this.store.on('loadexception', this.onLoadException, this);
17817         
17818         if(this.editable){
17819             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17820                 "up" : function(e){
17821                     this.inKeyMode = true;
17822                     this.selectPrev();
17823                 },
17824
17825                 "down" : function(e){
17826                     this.inKeyMode = true;
17827                     this.selectNext();
17828                 },
17829
17830                 "enter" : function(e){
17831                     if(this.fireEvent("specialkey", this, e)){
17832                         this.onViewClick(false);
17833                     }
17834                     
17835                     return true;
17836                 },
17837
17838                 "esc" : function(e){
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                 },
17841
17842                 "tab" : function(e){
17843                     this.fireEvent("specialkey", this, e);
17844                     
17845                     this.onTickableFooterButtonClick(e, false, false);
17846                     
17847                     return true;
17848                 },
17849
17850                 scope : this,
17851
17852                 doRelay : function(e, fn, key){
17853                     if(this.scope.isExpanded()){
17854                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17855                     }
17856                     return true;
17857                 },
17858
17859                 forceKeyDown: true
17860             });
17861         }
17862         
17863         this.queryDelay = Math.max(this.queryDelay || 10,
17864                 this.mode == 'local' ? 10 : 250);
17865         
17866         
17867         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17868         
17869         if(this.typeAhead){
17870             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17871         }
17872         
17873         if(this.editable !== false){
17874             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17875         }
17876         
17877         this.indicator = this.indicatorEl();
17878         
17879         if(this.indicator){
17880             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17881             this.indicator.hide();
17882         }
17883         
17884     },
17885
17886     onDestroy : function(){
17887         if(this.view){
17888             this.view.setStore(null);
17889             this.view.el.removeAllListeners();
17890             this.view.el.remove();
17891             this.view.purgeListeners();
17892         }
17893         if(this.list){
17894             this.list.dom.innerHTML  = '';
17895         }
17896         
17897         if(this.store){
17898             this.store.un('beforeload', this.onBeforeLoad, this);
17899             this.store.un('load', this.onLoad, this);
17900             this.store.un('loadexception', this.onLoadException, this);
17901         }
17902         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17903     },
17904
17905     // private
17906     fireKey : function(e){
17907         if(e.isNavKeyPress() && !this.list.isVisible()){
17908             this.fireEvent("specialkey", this, e);
17909         }
17910     },
17911
17912     // private
17913     onResize: function(w, h)
17914     {
17915         
17916         
17917 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17918 //        
17919 //        if(typeof w != 'number'){
17920 //            // we do not handle it!?!?
17921 //            return;
17922 //        }
17923 //        var tw = this.trigger.getWidth();
17924 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17925 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17926 //        var x = w - tw;
17927 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17928 //            
17929 //        //this.trigger.setStyle('left', x+'px');
17930 //        
17931 //        if(this.list && this.listWidth === undefined){
17932 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17933 //            this.list.setWidth(lw);
17934 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17935 //        }
17936         
17937     
17938         
17939     },
17940
17941     /**
17942      * Allow or prevent the user from directly editing the field text.  If false is passed,
17943      * the user will only be able to select from the items defined in the dropdown list.  This method
17944      * is the runtime equivalent of setting the 'editable' config option at config time.
17945      * @param {Boolean} value True to allow the user to directly edit the field text
17946      */
17947     setEditable : function(value){
17948         if(value == this.editable){
17949             return;
17950         }
17951         this.editable = value;
17952         if(!value){
17953             this.inputEl().dom.setAttribute('readOnly', true);
17954             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17955             this.inputEl().addClass('x-combo-noedit');
17956         }else{
17957             this.inputEl().dom.removeAttribute('readOnly');
17958             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17959             this.inputEl().removeClass('x-combo-noedit');
17960         }
17961     },
17962
17963     // private
17964     
17965     onBeforeLoad : function(combo,opts){
17966         if(!this.hasFocus){
17967             return;
17968         }
17969          if (!opts.add) {
17970             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17971          }
17972         this.restrictHeight();
17973         this.selectedIndex = -1;
17974     },
17975
17976     // private
17977     onLoad : function(){
17978         
17979         this.hasQuery = false;
17980         
17981         if(!this.hasFocus){
17982             return;
17983         }
17984         
17985         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17986             this.loading.hide();
17987         }
17988         
17989         if(this.store.getCount() > 0){
17990             
17991             this.expand();
17992             this.restrictHeight();
17993             if(this.lastQuery == this.allQuery){
17994                 if(this.editable && !this.tickable){
17995                     this.inputEl().dom.select();
17996                 }
17997                 
17998                 if(
17999                     !this.selectByValue(this.value, true) &&
18000                     this.autoFocus && 
18001                     (
18002                         !this.store.lastOptions ||
18003                         typeof(this.store.lastOptions.add) == 'undefined' || 
18004                         this.store.lastOptions.add != true
18005                     )
18006                 ){
18007                     this.select(0, true);
18008                 }
18009             }else{
18010                 if(this.autoFocus){
18011                     this.selectNext();
18012                 }
18013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18014                     this.taTask.delay(this.typeAheadDelay);
18015                 }
18016             }
18017         }else{
18018             this.onEmptyResults();
18019         }
18020         
18021         //this.el.focus();
18022     },
18023     // private
18024     onLoadException : function()
18025     {
18026         this.hasQuery = false;
18027         
18028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18029             this.loading.hide();
18030         }
18031         
18032         if(this.tickable && this.editable){
18033             return;
18034         }
18035         
18036         this.collapse();
18037         // only causes errors at present
18038         //Roo.log(this.store.reader.jsonData);
18039         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18040             // fixme
18041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18042         //}
18043         
18044         
18045     },
18046     // private
18047     onTypeAhead : function(){
18048         if(this.store.getCount() > 0){
18049             var r = this.store.getAt(0);
18050             var newValue = r.data[this.displayField];
18051             var len = newValue.length;
18052             var selStart = this.getRawValue().length;
18053             
18054             if(selStart != len){
18055                 this.setRawValue(newValue);
18056                 this.selectText(selStart, newValue.length);
18057             }
18058         }
18059     },
18060
18061     // private
18062     onSelect : function(record, index){
18063         
18064         if(this.fireEvent('beforeselect', this, record, index) !== false){
18065         
18066             this.setFromData(index > -1 ? record.data : false);
18067             
18068             this.collapse();
18069             this.fireEvent('select', this, record, index);
18070         }
18071     },
18072
18073     /**
18074      * Returns the currently selected field value or empty string if no value is set.
18075      * @return {String} value The selected value
18076      */
18077     getValue : function()
18078     {
18079         if(Roo.isIOS && this.useNativeIOS){
18080             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18081         }
18082         
18083         if(this.multiple){
18084             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18085         }
18086         
18087         if(this.valueField){
18088             return typeof this.value != 'undefined' ? this.value : '';
18089         }else{
18090             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18091         }
18092     },
18093     
18094     getRawValue : function()
18095     {
18096         if(Roo.isIOS && this.useNativeIOS){
18097             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18098         }
18099         
18100         var v = this.inputEl().getValue();
18101         
18102         return v;
18103     },
18104
18105     /**
18106      * Clears any text/value currently set in the field
18107      */
18108     clearValue : function(){
18109         
18110         if(this.hiddenField){
18111             this.hiddenField.dom.value = '';
18112         }
18113         this.value = '';
18114         this.setRawValue('');
18115         this.lastSelectionText = '';
18116         this.lastData = false;
18117         
18118         var close = this.closeTriggerEl();
18119         
18120         if(close){
18121             close.hide();
18122         }
18123         
18124         this.validate();
18125         
18126     },
18127
18128     /**
18129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18130      * will be displayed in the field.  If the value does not match the data value of an existing item,
18131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18132      * Otherwise the field will be blank (although the value will still be set).
18133      * @param {String} value The value to match
18134      */
18135     setValue : function(v)
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             this.setIOSValue(v);
18139             return;
18140         }
18141         
18142         if(this.multiple){
18143             this.syncValue();
18144             return;
18145         }
18146         
18147         var text = v;
18148         if(this.valueField){
18149             var r = this.findRecord(this.valueField, v);
18150             if(r){
18151                 text = r.data[this.displayField];
18152             }else if(this.valueNotFoundText !== undefined){
18153                 text = this.valueNotFoundText;
18154             }
18155         }
18156         this.lastSelectionText = text;
18157         if(this.hiddenField){
18158             this.hiddenField.dom.value = v;
18159         }
18160         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18161         this.value = v;
18162         
18163         var close = this.closeTriggerEl();
18164         
18165         if(close){
18166             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18167         }
18168         
18169         this.validate();
18170     },
18171     /**
18172      * @property {Object} the last set data for the element
18173      */
18174     
18175     lastData : false,
18176     /**
18177      * Sets the value of the field based on a object which is related to the record format for the store.
18178      * @param {Object} value the value to set as. or false on reset?
18179      */
18180     setFromData : function(o){
18181         
18182         if(this.multiple){
18183             this.addItem(o);
18184             return;
18185         }
18186             
18187         var dv = ''; // display value
18188         var vv = ''; // value value..
18189         this.lastData = o;
18190         if (this.displayField) {
18191             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18192         } else {
18193             // this is an error condition!!!
18194             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18195         }
18196         
18197         if(this.valueField){
18198             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18199         }
18200         
18201         var close = this.closeTriggerEl();
18202         
18203         if(close){
18204             if(dv.length || vv * 1 > 0){
18205                 close.show() ;
18206                 this.blockFocus=true;
18207             } else {
18208                 close.hide();
18209             }             
18210         }
18211         
18212         if(this.hiddenField){
18213             this.hiddenField.dom.value = vv;
18214             
18215             this.lastSelectionText = dv;
18216             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18217             this.value = vv;
18218             return;
18219         }
18220         // no hidden field.. - we store the value in 'value', but still display
18221         // display field!!!!
18222         this.lastSelectionText = dv;
18223         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224         this.value = vv;
18225         
18226         
18227         
18228     },
18229     // private
18230     reset : function(){
18231         // overridden so that last data is reset..
18232         
18233         if(this.multiple){
18234             this.clearItem();
18235             return;
18236         }
18237         
18238         this.setValue(this.originalValue);
18239         //this.clearInvalid();
18240         this.lastData = false;
18241         if (this.view) {
18242             this.view.clearSelections();
18243         }
18244         
18245         this.validate();
18246     },
18247     // private
18248     findRecord : function(prop, value){
18249         var record;
18250         if(this.store.getCount() > 0){
18251             this.store.each(function(r){
18252                 if(r.data[prop] == value){
18253                     record = r;
18254                     return false;
18255                 }
18256                 return true;
18257             });
18258         }
18259         return record;
18260     },
18261     
18262     getName: function()
18263     {
18264         // returns hidden if it's set..
18265         if (!this.rendered) {return ''};
18266         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18267         
18268     },
18269     // private
18270     onViewMove : function(e, t){
18271         this.inKeyMode = false;
18272     },
18273
18274     // private
18275     onViewOver : function(e, t){
18276         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18277             return;
18278         }
18279         var item = this.view.findItemFromChild(t);
18280         
18281         if(item){
18282             var index = this.view.indexOf(item);
18283             this.select(index, false);
18284         }
18285     },
18286
18287     // private
18288     onViewClick : function(view, doFocus, el, e)
18289     {
18290         var index = this.view.getSelectedIndexes()[0];
18291         
18292         var r = this.store.getAt(index);
18293         
18294         if(this.tickable){
18295             
18296             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18297                 return;
18298             }
18299             
18300             var rm = false;
18301             var _this = this;
18302             
18303             Roo.each(this.tickItems, function(v,k){
18304                 
18305                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18306                     Roo.log(v);
18307                     _this.tickItems.splice(k, 1);
18308                     
18309                     if(typeof(e) == 'undefined' && view == false){
18310                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18311                     }
18312                     
18313                     rm = true;
18314                     return;
18315                 }
18316             });
18317             
18318             if(rm){
18319                 return;
18320             }
18321             
18322             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18323                 this.tickItems.push(r.data);
18324             }
18325             
18326             if(typeof(e) == 'undefined' && view == false){
18327                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18328             }
18329                     
18330             return;
18331         }
18332         
18333         if(r){
18334             this.onSelect(r, index);
18335         }
18336         if(doFocus !== false && !this.blockFocus){
18337             this.inputEl().focus();
18338         }
18339     },
18340
18341     // private
18342     restrictHeight : function(){
18343         //this.innerList.dom.style.height = '';
18344         //var inner = this.innerList.dom;
18345         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18346         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18347         //this.list.beginUpdate();
18348         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         this.list.alignTo(this.inputEl(), this.listAlign);
18351         //this.list.endUpdate();
18352     },
18353
18354     // private
18355     onEmptyResults : function(){
18356         
18357         if(this.tickable && this.editable){
18358             this.hasFocus = false;
18359             this.restrictHeight();
18360             return;
18361         }
18362         
18363         this.collapse();
18364     },
18365
18366     /**
18367      * Returns true if the dropdown list is expanded, else false.
18368      */
18369     isExpanded : function(){
18370         return this.list.isVisible();
18371     },
18372
18373     /**
18374      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18376      * @param {String} value The data value of the item to select
18377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18378      * selected item if it is not currently in view (defaults to true)
18379      * @return {Boolean} True if the value matched an item in the list, else false
18380      */
18381     selectByValue : function(v, scrollIntoView){
18382         if(v !== undefined && v !== null){
18383             var r = this.findRecord(this.valueField || this.displayField, v);
18384             if(r){
18385                 this.select(this.store.indexOf(r), scrollIntoView);
18386                 return true;
18387             }
18388         }
18389         return false;
18390     },
18391
18392     /**
18393      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18395      * @param {Number} index The zero-based index of the list item to select
18396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18397      * selected item if it is not currently in view (defaults to true)
18398      */
18399     select : function(index, scrollIntoView){
18400         this.selectedIndex = index;
18401         this.view.select(index);
18402         if(scrollIntoView !== false){
18403             var el = this.view.getNode(index);
18404             /*
18405              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18406              */
18407             if(el){
18408                 this.list.scrollChildIntoView(el, false);
18409             }
18410         }
18411     },
18412
18413     // private
18414     selectNext : function(){
18415         var ct = this.store.getCount();
18416         if(ct > 0){
18417             if(this.selectedIndex == -1){
18418                 this.select(0);
18419             }else if(this.selectedIndex < ct-1){
18420                 this.select(this.selectedIndex+1);
18421             }
18422         }
18423     },
18424
18425     // private
18426     selectPrev : function(){
18427         var ct = this.store.getCount();
18428         if(ct > 0){
18429             if(this.selectedIndex == -1){
18430                 this.select(0);
18431             }else if(this.selectedIndex != 0){
18432                 this.select(this.selectedIndex-1);
18433             }
18434         }
18435     },
18436
18437     // private
18438     onKeyUp : function(e){
18439         if(this.editable !== false && !e.isSpecialKey()){
18440             this.lastKey = e.getKey();
18441             this.dqTask.delay(this.queryDelay);
18442         }
18443     },
18444
18445     // private
18446     validateBlur : function(){
18447         return !this.list || !this.list.isVisible();   
18448     },
18449
18450     // private
18451     initQuery : function(){
18452         
18453         var v = this.getRawValue();
18454         
18455         if(this.tickable && this.editable){
18456             v = this.tickableInputEl().getValue();
18457         }
18458         
18459         this.doQuery(v);
18460     },
18461
18462     // private
18463     doForce : function(){
18464         if(this.inputEl().dom.value.length > 0){
18465             this.inputEl().dom.value =
18466                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18467              
18468         }
18469     },
18470
18471     /**
18472      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18473      * query allowing the query action to be canceled if needed.
18474      * @param {String} query The SQL query to execute
18475      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18476      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18477      * saved in the current store (defaults to false)
18478      */
18479     doQuery : function(q, forceAll){
18480         
18481         if(q === undefined || q === null){
18482             q = '';
18483         }
18484         var qe = {
18485             query: q,
18486             forceAll: forceAll,
18487             combo: this,
18488             cancel:false
18489         };
18490         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18491             return false;
18492         }
18493         q = qe.query;
18494         
18495         forceAll = qe.forceAll;
18496         if(forceAll === true || (q.length >= this.minChars)){
18497             
18498             this.hasQuery = true;
18499             
18500             if(this.lastQuery != q || this.alwaysQuery){
18501                 this.lastQuery = q;
18502                 if(this.mode == 'local'){
18503                     this.selectedIndex = -1;
18504                     if(forceAll){
18505                         this.store.clearFilter();
18506                     }else{
18507                         
18508                         if(this.specialFilter){
18509                             this.fireEvent('specialfilter', this);
18510                             this.onLoad();
18511                             return;
18512                         }
18513                         
18514                         this.store.filter(this.displayField, q);
18515                     }
18516                     
18517                     this.store.fireEvent("datachanged", this.store);
18518                     
18519                     this.onLoad();
18520                     
18521                     
18522                 }else{
18523                     
18524                     this.store.baseParams[this.queryParam] = q;
18525                     
18526                     var options = {params : this.getParams(q)};
18527                     
18528                     if(this.loadNext){
18529                         options.add = true;
18530                         options.params.start = this.page * this.pageSize;
18531                     }
18532                     
18533                     this.store.load(options);
18534                     
18535                     /*
18536                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18537                      *  we should expand the list on onLoad
18538                      *  so command out it
18539                      */
18540 //                    this.expand();
18541                 }
18542             }else{
18543                 this.selectedIndex = -1;
18544                 this.onLoad();   
18545             }
18546         }
18547         
18548         this.loadNext = false;
18549     },
18550     
18551     // private
18552     getParams : function(q){
18553         var p = {};
18554         //p[this.queryParam] = q;
18555         
18556         if(this.pageSize){
18557             p.start = 0;
18558             p.limit = this.pageSize;
18559         }
18560         return p;
18561     },
18562
18563     /**
18564      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18565      */
18566     collapse : function(){
18567         if(!this.isExpanded()){
18568             return;
18569         }
18570         
18571         this.list.hide();
18572         
18573         this.hasFocus = false;
18574         
18575         if(this.tickable){
18576             this.okBtn.hide();
18577             this.cancelBtn.hide();
18578             this.trigger.show();
18579             
18580             if(this.editable){
18581                 this.tickableInputEl().dom.value = '';
18582                 this.tickableInputEl().blur();
18583             }
18584             
18585         }
18586         
18587         Roo.get(document).un('mousedown', this.collapseIf, this);
18588         Roo.get(document).un('mousewheel', this.collapseIf, this);
18589         if (!this.editable) {
18590             Roo.get(document).un('keydown', this.listKeyPress, this);
18591         }
18592         this.fireEvent('collapse', this);
18593         
18594         this.validate();
18595     },
18596
18597     // private
18598     collapseIf : function(e){
18599         var in_combo  = e.within(this.el);
18600         var in_list =  e.within(this.list);
18601         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18602         
18603         if (in_combo || in_list || is_list) {
18604             //e.stopPropagation();
18605             return;
18606         }
18607         
18608         if(this.tickable){
18609             this.onTickableFooterButtonClick(e, false, false);
18610         }
18611
18612         this.collapse();
18613         
18614     },
18615
18616     /**
18617      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18618      */
18619     expand : function(){
18620        
18621         if(this.isExpanded() || !this.hasFocus){
18622             return;
18623         }
18624         
18625         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18626         this.list.setWidth(lw);
18627         
18628         Roo.log('expand');
18629         
18630         this.list.show();
18631         
18632         this.restrictHeight();
18633         
18634         if(this.tickable){
18635             
18636             this.tickItems = Roo.apply([], this.item);
18637             
18638             this.okBtn.show();
18639             this.cancelBtn.show();
18640             this.trigger.hide();
18641             
18642             if(this.editable){
18643                 this.tickableInputEl().focus();
18644             }
18645             
18646         }
18647         
18648         Roo.get(document).on('mousedown', this.collapseIf, this);
18649         Roo.get(document).on('mousewheel', this.collapseIf, this);
18650         if (!this.editable) {
18651             Roo.get(document).on('keydown', this.listKeyPress, this);
18652         }
18653         
18654         this.fireEvent('expand', this);
18655     },
18656
18657     // private
18658     // Implements the default empty TriggerField.onTriggerClick function
18659     onTriggerClick : function(e)
18660     {
18661         Roo.log('trigger click');
18662         
18663         if(this.disabled || !this.triggerList){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         
18670         if(this.isExpanded()){
18671             this.collapse();
18672             if (!this.blockFocus) {
18673                 this.inputEl().focus();
18674             }
18675             
18676         }else {
18677             this.hasFocus = true;
18678             if(this.triggerAction == 'all') {
18679                 this.doQuery(this.allQuery, true);
18680             } else {
18681                 this.doQuery(this.getRawValue());
18682             }
18683             if (!this.blockFocus) {
18684                 this.inputEl().focus();
18685             }
18686         }
18687     },
18688     
18689     onTickableTriggerClick : function(e)
18690     {
18691         if(this.disabled){
18692             return;
18693         }
18694         
18695         this.page = 0;
18696         this.loadNext = false;
18697         this.hasFocus = true;
18698         
18699         if(this.triggerAction == 'all') {
18700             this.doQuery(this.allQuery, true);
18701         } else {
18702             this.doQuery(this.getRawValue());
18703         }
18704     },
18705     
18706     onSearchFieldClick : function(e)
18707     {
18708         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18709             this.onTickableFooterButtonClick(e, false, false);
18710             return;
18711         }
18712         
18713         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18714             return;
18715         }
18716         
18717         this.page = 0;
18718         this.loadNext = false;
18719         this.hasFocus = true;
18720         
18721         if(this.triggerAction == 'all') {
18722             this.doQuery(this.allQuery, true);
18723         } else {
18724             this.doQuery(this.getRawValue());
18725         }
18726     },
18727     
18728     listKeyPress : function(e)
18729     {
18730         //Roo.log('listkeypress');
18731         // scroll to first matching element based on key pres..
18732         if (e.isSpecialKey()) {
18733             return false;
18734         }
18735         var k = String.fromCharCode(e.getKey()).toUpperCase();
18736         //Roo.log(k);
18737         var match  = false;
18738         var csel = this.view.getSelectedNodes();
18739         var cselitem = false;
18740         if (csel.length) {
18741             var ix = this.view.indexOf(csel[0]);
18742             cselitem  = this.store.getAt(ix);
18743             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18744                 cselitem = false;
18745             }
18746             
18747         }
18748         
18749         this.store.each(function(v) { 
18750             if (cselitem) {
18751                 // start at existing selection.
18752                 if (cselitem.id == v.id) {
18753                     cselitem = false;
18754                 }
18755                 return true;
18756             }
18757                 
18758             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18759                 match = this.store.indexOf(v);
18760                 return false;
18761             }
18762             return true;
18763         }, this);
18764         
18765         if (match === false) {
18766             return true; // no more action?
18767         }
18768         // scroll to?
18769         this.view.select(match);
18770         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18771         sn.scrollIntoView(sn.dom.parentNode, false);
18772     },
18773     
18774     onViewScroll : function(e, t){
18775         
18776         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18777             return;
18778         }
18779         
18780         this.hasQuery = true;
18781         
18782         this.loading = this.list.select('.loading', true).first();
18783         
18784         if(this.loading === null){
18785             this.list.createChild({
18786                 tag: 'div',
18787                 cls: 'loading roo-select2-more-results roo-select2-active',
18788                 html: 'Loading more results...'
18789             });
18790             
18791             this.loading = this.list.select('.loading', true).first();
18792             
18793             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18794             
18795             this.loading.hide();
18796         }
18797         
18798         this.loading.show();
18799         
18800         var _combo = this;
18801         
18802         this.page++;
18803         this.loadNext = true;
18804         
18805         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18806         
18807         return;
18808     },
18809     
18810     addItem : function(o)
18811     {   
18812         var dv = ''; // display value
18813         
18814         if (this.displayField) {
18815             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18816         } else {
18817             // this is an error condition!!!
18818             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18819         }
18820         
18821         if(!dv.length){
18822             return;
18823         }
18824         
18825         var choice = this.choices.createChild({
18826             tag: 'li',
18827             cls: 'roo-select2-search-choice',
18828             cn: [
18829                 {
18830                     tag: 'div',
18831                     html: dv
18832                 },
18833                 {
18834                     tag: 'a',
18835                     href: '#',
18836                     cls: 'roo-select2-search-choice-close fa fa-times',
18837                     tabindex: '-1'
18838                 }
18839             ]
18840             
18841         }, this.searchField);
18842         
18843         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18844         
18845         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18846         
18847         this.item.push(o);
18848         
18849         this.lastData = o;
18850         
18851         this.syncValue();
18852         
18853         this.inputEl().dom.value = '';
18854         
18855         this.validate();
18856     },
18857     
18858     onRemoveItem : function(e, _self, o)
18859     {
18860         e.preventDefault();
18861         
18862         this.lastItem = Roo.apply([], this.item);
18863         
18864         var index = this.item.indexOf(o.data) * 1;
18865         
18866         if( index < 0){
18867             Roo.log('not this item?!');
18868             return;
18869         }
18870         
18871         this.item.splice(index, 1);
18872         o.item.remove();
18873         
18874         this.syncValue();
18875         
18876         this.fireEvent('remove', this, e);
18877         
18878         this.validate();
18879         
18880     },
18881     
18882     syncValue : function()
18883     {
18884         if(!this.item.length){
18885             this.clearValue();
18886             return;
18887         }
18888             
18889         var value = [];
18890         var _this = this;
18891         Roo.each(this.item, function(i){
18892             if(_this.valueField){
18893                 value.push(i[_this.valueField]);
18894                 return;
18895             }
18896
18897             value.push(i);
18898         });
18899
18900         this.value = value.join(',');
18901
18902         if(this.hiddenField){
18903             this.hiddenField.dom.value = this.value;
18904         }
18905         
18906         this.store.fireEvent("datachanged", this.store);
18907         
18908         this.validate();
18909     },
18910     
18911     clearItem : function()
18912     {
18913         if(!this.multiple){
18914             return;
18915         }
18916         
18917         this.item = [];
18918         
18919         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18920            c.remove();
18921         });
18922         
18923         this.syncValue();
18924         
18925         this.validate();
18926         
18927         if(this.tickable && !Roo.isTouch){
18928             this.view.refresh();
18929         }
18930     },
18931     
18932     inputEl: function ()
18933     {
18934         if(Roo.isIOS && this.useNativeIOS){
18935             return this.el.select('select.roo-ios-select', true).first();
18936         }
18937         
18938         if(Roo.isTouch && this.mobileTouchView){
18939             return this.el.select('input.form-control',true).first();
18940         }
18941         
18942         if(this.tickable){
18943             return this.searchField;
18944         }
18945         
18946         return this.el.select('input.form-control',true).first();
18947     },
18948     
18949     onTickableFooterButtonClick : function(e, btn, el)
18950     {
18951         e.preventDefault();
18952         
18953         this.lastItem = Roo.apply([], this.item);
18954         
18955         if(btn && btn.name == 'cancel'){
18956             this.tickItems = Roo.apply([], this.item);
18957             this.collapse();
18958             return;
18959         }
18960         
18961         this.clearItem();
18962         
18963         var _this = this;
18964         
18965         Roo.each(this.tickItems, function(o){
18966             _this.addItem(o);
18967         });
18968         
18969         this.collapse();
18970         
18971     },
18972     
18973     validate : function()
18974     {
18975         if(this.getVisibilityEl().hasClass('hidden')){
18976             return true;
18977         }
18978         
18979         var v = this.getRawValue();
18980         
18981         if(this.multiple){
18982             v = this.getValue();
18983         }
18984         
18985         if(this.disabled || this.allowBlank || v.length){
18986             this.markValid();
18987             return true;
18988         }
18989         
18990         this.markInvalid();
18991         return false;
18992     },
18993     
18994     tickableInputEl : function()
18995     {
18996         if(!this.tickable || !this.editable){
18997             return this.inputEl();
18998         }
18999         
19000         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19001     },
19002     
19003     
19004     getAutoCreateTouchView : function()
19005     {
19006         var id = Roo.id();
19007         
19008         var cfg = {
19009             cls: 'form-group' //input-group
19010         };
19011         
19012         var input =  {
19013             tag: 'input',
19014             id : id,
19015             type : this.inputType,
19016             cls : 'form-control x-combo-noedit',
19017             autocomplete: 'new-password',
19018             placeholder : this.placeholder || '',
19019             readonly : true
19020         };
19021         
19022         if (this.name) {
19023             input.name = this.name;
19024         }
19025         
19026         if (this.size) {
19027             input.cls += ' input-' + this.size;
19028         }
19029         
19030         if (this.disabled) {
19031             input.disabled = true;
19032         }
19033         
19034         var inputblock = {
19035             cls : 'roo-combobox-wrap',
19036             cn : [
19037                 input
19038             ]
19039         };
19040         
19041         if(this.before){
19042             inputblock.cls += ' input-group';
19043             
19044             inputblock.cn.unshift({
19045                 tag :'span',
19046                 cls : 'input-group-addon input-group-prepend input-group-text',
19047                 html : this.before
19048             });
19049         }
19050         
19051         if(this.removable && !this.multiple){
19052             inputblock.cls += ' roo-removable';
19053             
19054             inputblock.cn.push({
19055                 tag: 'button',
19056                 html : 'x',
19057                 cls : 'roo-combo-removable-btn close'
19058             });
19059         }
19060
19061         if(this.hasFeedback && !this.allowBlank){
19062             
19063             inputblock.cls += ' has-feedback';
19064             
19065             inputblock.cn.push({
19066                 tag: 'span',
19067                 cls: 'glyphicon form-control-feedback'
19068             });
19069             
19070         }
19071         
19072         if (this.after) {
19073             
19074             inputblock.cls += (this.before) ? '' : ' input-group';
19075             
19076             inputblock.cn.push({
19077                 tag :'span',
19078                 cls : 'input-group-addon input-group-append input-group-text',
19079                 html : this.after
19080             });
19081         }
19082
19083         
19084         var ibwrap = inputblock;
19085         
19086         if(this.multiple){
19087             ibwrap = {
19088                 tag: 'ul',
19089                 cls: 'roo-select2-choices',
19090                 cn:[
19091                     {
19092                         tag: 'li',
19093                         cls: 'roo-select2-search-field',
19094                         cn: [
19095
19096                             inputblock
19097                         ]
19098                     }
19099                 ]
19100             };
19101         
19102             
19103         }
19104         
19105         var combobox = {
19106             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19107             cn: [
19108                 {
19109                     tag: 'input',
19110                     type : 'hidden',
19111                     cls: 'form-hidden-field'
19112                 },
19113                 ibwrap
19114             ]
19115         };
19116         
19117         if(!this.multiple && this.showToggleBtn){
19118             
19119             var caret = {
19120                 cls: 'caret'
19121             };
19122             
19123             if (this.caret != false) {
19124                 caret = {
19125                      tag: 'i',
19126                      cls: 'fa fa-' + this.caret
19127                 };
19128                 
19129             }
19130             
19131             combobox.cn.push({
19132                 tag :'span',
19133                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19134                 cn : [
19135                     Roo.bootstrap.version == 3 ? caret : '',
19136                     {
19137                         tag: 'span',
19138                         cls: 'combobox-clear',
19139                         cn  : [
19140                             {
19141                                 tag : 'i',
19142                                 cls: 'icon-remove'
19143                             }
19144                         ]
19145                     }
19146                 ]
19147
19148             })
19149         }
19150         
19151         if(this.multiple){
19152             combobox.cls += ' roo-select2-container-multi';
19153         }
19154         
19155         var required =  this.allowBlank ?  {
19156                     tag : 'i',
19157                     style: 'display: none'
19158                 } : {
19159                    tag : 'i',
19160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19161                    tooltip : 'This field is required'
19162                 };
19163         
19164         var align = this.labelAlign || this.parentLabelAlign();
19165         
19166         if (align ==='left' && this.fieldLabel.length) {
19167
19168             cfg.cn = [
19169                 required,
19170                 {
19171                     tag: 'label',
19172                     cls : 'control-label col-form-label',
19173                     html : this.fieldLabel
19174
19175                 },
19176                 {
19177                     cls : 'roo-combobox-wrap ', 
19178                     cn: [
19179                         combobox
19180                     ]
19181                 }
19182             ];
19183             
19184             var labelCfg = cfg.cn[1];
19185             var contentCfg = cfg.cn[2];
19186             
19187
19188             if(this.indicatorpos == 'right'){
19189                 cfg.cn = [
19190                     {
19191                         tag: 'label',
19192                         'for' :  id,
19193                         cls : 'control-label col-form-label',
19194                         cn : [
19195                             {
19196                                 tag : 'span',
19197                                 html : this.fieldLabel
19198                             },
19199                             required
19200                         ]
19201                     },
19202                     {
19203                         cls : "roo-combobox-wrap ",
19204                         cn: [
19205                             combobox
19206                         ]
19207                     }
19208
19209                 ];
19210                 
19211                 labelCfg = cfg.cn[0];
19212                 contentCfg = cfg.cn[1];
19213             }
19214             
19215            
19216             
19217             if(this.labelWidth > 12){
19218                 labelCfg.style = "width: " + this.labelWidth + 'px';
19219             }
19220            
19221             if(this.labelWidth < 13 && this.labelmd == 0){
19222                 this.labelmd = this.labelWidth;
19223             }
19224             
19225             if(this.labellg > 0){
19226                 labelCfg.cls += ' col-lg-' + this.labellg;
19227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19228             }
19229             
19230             if(this.labelmd > 0){
19231                 labelCfg.cls += ' col-md-' + this.labelmd;
19232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19233             }
19234             
19235             if(this.labelsm > 0){
19236                 labelCfg.cls += ' col-sm-' + this.labelsm;
19237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19238             }
19239             
19240             if(this.labelxs > 0){
19241                 labelCfg.cls += ' col-xs-' + this.labelxs;
19242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19243             }
19244                 
19245                 
19246         } else if ( this.fieldLabel.length) {
19247             cfg.cn = [
19248                required,
19249                 {
19250                     tag: 'label',
19251                     cls : 'control-label',
19252                     html : this.fieldLabel
19253
19254                 },
19255                 {
19256                     cls : '', 
19257                     cn: [
19258                         combobox
19259                     ]
19260                 }
19261             ];
19262             
19263             if(this.indicatorpos == 'right'){
19264                 cfg.cn = [
19265                     {
19266                         tag: 'label',
19267                         cls : 'control-label',
19268                         html : this.fieldLabel,
19269                         cn : [
19270                             required
19271                         ]
19272                     },
19273                     {
19274                         cls : '', 
19275                         cn: [
19276                             combobox
19277                         ]
19278                     }
19279                 ];
19280             }
19281         } else {
19282             cfg.cn = combobox;    
19283         }
19284         
19285         
19286         var settings = this;
19287         
19288         ['xs','sm','md','lg'].map(function(size){
19289             if (settings[size]) {
19290                 cfg.cls += ' col-' + size + '-' + settings[size];
19291             }
19292         });
19293         
19294         return cfg;
19295     },
19296     
19297     initTouchView : function()
19298     {
19299         this.renderTouchView();
19300         
19301         this.touchViewEl.on('scroll', function(){
19302             this.el.dom.scrollTop = 0;
19303         }, this);
19304         
19305         this.originalValue = this.getValue();
19306         
19307         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19308         
19309         this.inputEl().on("click", this.showTouchView, this);
19310         if (this.triggerEl) {
19311             this.triggerEl.on("click", this.showTouchView, this);
19312         }
19313         
19314         
19315         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19316         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19317         
19318         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19319         
19320         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19321         this.store.on('load', this.onTouchViewLoad, this);
19322         this.store.on('loadexception', this.onTouchViewLoadException, this);
19323         
19324         if(this.hiddenName){
19325             
19326             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19327             
19328             this.hiddenField.dom.value =
19329                 this.hiddenValue !== undefined ? this.hiddenValue :
19330                 this.value !== undefined ? this.value : '';
19331         
19332             this.el.dom.removeAttribute('name');
19333             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19334         }
19335         
19336         if(this.multiple){
19337             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19338             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19339         }
19340         
19341         if(this.removable && !this.multiple){
19342             var close = this.closeTriggerEl();
19343             if(close){
19344                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19345                 close.on('click', this.removeBtnClick, this, close);
19346             }
19347         }
19348         /*
19349          * fix the bug in Safari iOS8
19350          */
19351         this.inputEl().on("focus", function(e){
19352             document.activeElement.blur();
19353         }, this);
19354         
19355         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19356         
19357         return;
19358         
19359         
19360     },
19361     
19362     renderTouchView : function()
19363     {
19364         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19365         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         
19367         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19368         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19371         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         this.touchViewBodyEl.setStyle('overflow', 'auto');
19373         
19374         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19375         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376         
19377         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19378         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380     },
19381     
19382     showTouchView : function()
19383     {
19384         if(this.disabled){
19385             return;
19386         }
19387         
19388         this.touchViewHeaderEl.hide();
19389
19390         if(this.modalTitle.length){
19391             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19392             this.touchViewHeaderEl.show();
19393         }
19394
19395         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19396         this.touchViewEl.show();
19397
19398         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19399         
19400         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19401         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19402
19403         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19404
19405         if(this.modalTitle.length){
19406             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19407         }
19408         
19409         this.touchViewBodyEl.setHeight(bodyHeight);
19410
19411         if(this.animate){
19412             var _this = this;
19413             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19414         }else{
19415             this.touchViewEl.addClass(['in','show']);
19416         }
19417         
19418         if(this._touchViewMask){
19419             Roo.get(document.body).addClass("x-body-masked");
19420             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19421             this._touchViewMask.setStyle('z-index', 10000);
19422             this._touchViewMask.addClass('show');
19423         }
19424         
19425         this.doTouchViewQuery();
19426         
19427     },
19428     
19429     hideTouchView : function()
19430     {
19431         this.touchViewEl.removeClass(['in','show']);
19432
19433         if(this.animate){
19434             var _this = this;
19435             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19436         }else{
19437             this.touchViewEl.setStyle('display', 'none');
19438         }
19439         
19440         if(this._touchViewMask){
19441             this._touchViewMask.removeClass('show');
19442             Roo.get(document.body).removeClass("x-body-masked");
19443         }
19444     },
19445     
19446     setTouchViewValue : function()
19447     {
19448         if(this.multiple){
19449             this.clearItem();
19450         
19451             var _this = this;
19452
19453             Roo.each(this.tickItems, function(o){
19454                 this.addItem(o);
19455             }, this);
19456         }
19457         
19458         this.hideTouchView();
19459     },
19460     
19461     doTouchViewQuery : function()
19462     {
19463         var qe = {
19464             query: '',
19465             forceAll: true,
19466             combo: this,
19467             cancel:false
19468         };
19469         
19470         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19471             return false;
19472         }
19473         
19474         if(!this.alwaysQuery || this.mode == 'local'){
19475             this.onTouchViewLoad();
19476             return;
19477         }
19478         
19479         this.store.load();
19480     },
19481     
19482     onTouchViewBeforeLoad : function(combo,opts)
19483     {
19484         return;
19485     },
19486
19487     // private
19488     onTouchViewLoad : function()
19489     {
19490         if(this.store.getCount() < 1){
19491             this.onTouchViewEmptyResults();
19492             return;
19493         }
19494         
19495         this.clearTouchView();
19496         
19497         var rawValue = this.getRawValue();
19498         
19499         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19500         
19501         this.tickItems = [];
19502         
19503         this.store.data.each(function(d, rowIndex){
19504             var row = this.touchViewListGroup.createChild(template);
19505             
19506             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19507                 row.addClass(d.data.cls);
19508             }
19509             
19510             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19511                 var cfg = {
19512                     data : d.data,
19513                     html : d.data[this.displayField]
19514                 };
19515                 
19516                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19517                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19518                 }
19519             }
19520             row.removeClass('selected');
19521             if(!this.multiple && this.valueField &&
19522                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19523             {
19524                 // radio buttons..
19525                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19526                 row.addClass('selected');
19527             }
19528             
19529             if(this.multiple && this.valueField &&
19530                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19531             {
19532                 
19533                 // checkboxes...
19534                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19535                 this.tickItems.push(d.data);
19536             }
19537             
19538             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19539             
19540         }, this);
19541         
19542         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19543         
19544         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19545
19546         if(this.modalTitle.length){
19547             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19548         }
19549
19550         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19551         
19552         if(this.mobile_restrict_height && listHeight < bodyHeight){
19553             this.touchViewBodyEl.setHeight(listHeight);
19554         }
19555         
19556         var _this = this;
19557         
19558         if(firstChecked && listHeight > bodyHeight){
19559             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19560         }
19561         
19562     },
19563     
19564     onTouchViewLoadException : function()
19565     {
19566         this.hideTouchView();
19567     },
19568     
19569     onTouchViewEmptyResults : function()
19570     {
19571         this.clearTouchView();
19572         
19573         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19574         
19575         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19576         
19577     },
19578     
19579     clearTouchView : function()
19580     {
19581         this.touchViewListGroup.dom.innerHTML = '';
19582     },
19583     
19584     onTouchViewClick : function(e, el, o)
19585     {
19586         e.preventDefault();
19587         
19588         var row = o.row;
19589         var rowIndex = o.rowIndex;
19590         
19591         var r = this.store.getAt(rowIndex);
19592         
19593         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19594             
19595             if(!this.multiple){
19596                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19597                     c.dom.removeAttribute('checked');
19598                 }, this);
19599
19600                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19601
19602                 this.setFromData(r.data);
19603
19604                 var close = this.closeTriggerEl();
19605
19606                 if(close){
19607                     close.show();
19608                 }
19609
19610                 this.hideTouchView();
19611
19612                 this.fireEvent('select', this, r, rowIndex);
19613
19614                 return;
19615             }
19616
19617             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19618                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19619                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19620                 return;
19621             }
19622
19623             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19624             this.addItem(r.data);
19625             this.tickItems.push(r.data);
19626         }
19627     },
19628     
19629     getAutoCreateNativeIOS : function()
19630     {
19631         var cfg = {
19632             cls: 'form-group' //input-group,
19633         };
19634         
19635         var combobox =  {
19636             tag: 'select',
19637             cls : 'roo-ios-select'
19638         };
19639         
19640         if (this.name) {
19641             combobox.name = this.name;
19642         }
19643         
19644         if (this.disabled) {
19645             combobox.disabled = true;
19646         }
19647         
19648         var settings = this;
19649         
19650         ['xs','sm','md','lg'].map(function(size){
19651             if (settings[size]) {
19652                 cfg.cls += ' col-' + size + '-' + settings[size];
19653             }
19654         });
19655         
19656         cfg.cn = combobox;
19657         
19658         return cfg;
19659         
19660     },
19661     
19662     initIOSView : function()
19663     {
19664         this.store.on('load', this.onIOSViewLoad, this);
19665         
19666         return;
19667     },
19668     
19669     onIOSViewLoad : function()
19670     {
19671         if(this.store.getCount() < 1){
19672             return;
19673         }
19674         
19675         this.clearIOSView();
19676         
19677         if(this.allowBlank) {
19678             
19679             var default_text = '-- SELECT --';
19680             
19681             if(this.placeholder.length){
19682                 default_text = this.placeholder;
19683             }
19684             
19685             if(this.emptyTitle.length){
19686                 default_text += ' - ' + this.emptyTitle + ' -';
19687             }
19688             
19689             var opt = this.inputEl().createChild({
19690                 tag: 'option',
19691                 value : 0,
19692                 html : default_text
19693             });
19694             
19695             var o = {};
19696             o[this.valueField] = 0;
19697             o[this.displayField] = default_text;
19698             
19699             this.ios_options.push({
19700                 data : o,
19701                 el : opt
19702             });
19703             
19704         }
19705         
19706         this.store.data.each(function(d, rowIndex){
19707             
19708             var html = '';
19709             
19710             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19711                 html = d.data[this.displayField];
19712             }
19713             
19714             var value = '';
19715             
19716             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19717                 value = d.data[this.valueField];
19718             }
19719             
19720             var option = {
19721                 tag: 'option',
19722                 value : value,
19723                 html : html
19724             };
19725             
19726             if(this.value == d.data[this.valueField]){
19727                 option['selected'] = true;
19728             }
19729             
19730             var opt = this.inputEl().createChild(option);
19731             
19732             this.ios_options.push({
19733                 data : d.data,
19734                 el : opt
19735             });
19736             
19737         }, this);
19738         
19739         this.inputEl().on('change', function(){
19740            this.fireEvent('select', this);
19741         }, this);
19742         
19743     },
19744     
19745     clearIOSView: function()
19746     {
19747         this.inputEl().dom.innerHTML = '';
19748         
19749         this.ios_options = [];
19750     },
19751     
19752     setIOSValue: function(v)
19753     {
19754         this.value = v;
19755         
19756         if(!this.ios_options){
19757             return;
19758         }
19759         
19760         Roo.each(this.ios_options, function(opts){
19761            
19762            opts.el.dom.removeAttribute('selected');
19763            
19764            if(opts.data[this.valueField] != v){
19765                return;
19766            }
19767            
19768            opts.el.dom.setAttribute('selected', true);
19769            
19770         }, this);
19771     }
19772
19773     /** 
19774     * @cfg {Boolean} grow 
19775     * @hide 
19776     */
19777     /** 
19778     * @cfg {Number} growMin 
19779     * @hide 
19780     */
19781     /** 
19782     * @cfg {Number} growMax 
19783     * @hide 
19784     */
19785     /**
19786      * @hide
19787      * @method autoSize
19788      */
19789 });
19790
19791 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19792     
19793     header : {
19794         tag: 'div',
19795         cls: 'modal-header',
19796         cn: [
19797             {
19798                 tag: 'h4',
19799                 cls: 'modal-title'
19800             }
19801         ]
19802     },
19803     
19804     body : {
19805         tag: 'div',
19806         cls: 'modal-body',
19807         cn: [
19808             {
19809                 tag: 'ul',
19810                 cls: 'list-group'
19811             }
19812         ]
19813     },
19814     
19815     listItemRadio : {
19816         tag: 'li',
19817         cls: 'list-group-item',
19818         cn: [
19819             {
19820                 tag: 'span',
19821                 cls: 'roo-combobox-list-group-item-value'
19822             },
19823             {
19824                 tag: 'div',
19825                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19826                 cn: [
19827                     {
19828                         tag: 'input',
19829                         type: 'radio'
19830                     },
19831                     {
19832                         tag: 'label'
19833                     }
19834                 ]
19835             }
19836         ]
19837     },
19838     
19839     listItemCheckbox : {
19840         tag: 'li',
19841         cls: 'list-group-item',
19842         cn: [
19843             {
19844                 tag: 'span',
19845                 cls: 'roo-combobox-list-group-item-value'
19846             },
19847             {
19848                 tag: 'div',
19849                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19850                 cn: [
19851                     {
19852                         tag: 'input',
19853                         type: 'checkbox'
19854                     },
19855                     {
19856                         tag: 'label'
19857                     }
19858                 ]
19859             }
19860         ]
19861     },
19862     
19863     emptyResult : {
19864         tag: 'div',
19865         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19866     },
19867     
19868     footer : {
19869         tag: 'div',
19870         cls: 'modal-footer',
19871         cn: [
19872             {
19873                 tag: 'div',
19874                 cls: 'row',
19875                 cn: [
19876                     {
19877                         tag: 'div',
19878                         cls: 'col-xs-6 text-left',
19879                         cn: {
19880                             tag: 'button',
19881                             cls: 'btn btn-danger roo-touch-view-cancel',
19882                             html: 'Cancel'
19883                         }
19884                     },
19885                     {
19886                         tag: 'div',
19887                         cls: 'col-xs-6 text-right',
19888                         cn: {
19889                             tag: 'button',
19890                             cls: 'btn btn-success roo-touch-view-ok',
19891                             html: 'OK'
19892                         }
19893                     }
19894                 ]
19895             }
19896         ]
19897         
19898     }
19899 });
19900
19901 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19902     
19903     touchViewTemplate : {
19904         tag: 'div',
19905         cls: 'modal fade roo-combobox-touch-view',
19906         cn: [
19907             {
19908                 tag: 'div',
19909                 cls: 'modal-dialog',
19910                 style : 'position:fixed', // we have to fix position....
19911                 cn: [
19912                     {
19913                         tag: 'div',
19914                         cls: 'modal-content',
19915                         cn: [
19916                             Roo.bootstrap.form.ComboBox.header,
19917                             Roo.bootstrap.form.ComboBox.body,
19918                             Roo.bootstrap.form.ComboBox.footer
19919                         ]
19920                     }
19921                 ]
19922             }
19923         ]
19924     }
19925 });/*
19926  * Based on:
19927  * Ext JS Library 1.1.1
19928  * Copyright(c) 2006-2007, Ext JS, LLC.
19929  *
19930  * Originally Released Under LGPL - original licence link has changed is not relivant.
19931  *
19932  * Fork - LGPL
19933  * <script type="text/javascript">
19934  */
19935
19936 /**
19937  * @class Roo.View
19938  * @extends Roo.util.Observable
19939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19940  * This class also supports single and multi selection modes. <br>
19941  * Create a data model bound view:
19942  <pre><code>
19943  var store = new Roo.data.Store(...);
19944
19945  var view = new Roo.View({
19946     el : "my-element",
19947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19948  
19949     singleSelect: true,
19950     selectedClass: "ydataview-selected",
19951     store: store
19952  });
19953
19954  // listen for node click?
19955  view.on("click", function(vw, index, node, e){
19956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19957  });
19958
19959  // load XML data
19960  dataModel.load("foobar.xml");
19961  </code></pre>
19962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19963  * <br><br>
19964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19966  * 
19967  * Note: old style constructor is still suported (container, template, config)
19968  * 
19969  * @constructor
19970  * Create a new View
19971  * @param {Object} config The config object
19972  * 
19973  */
19974 Roo.View = function(config, depreciated_tpl, depreciated_config){
19975     
19976     this.parent = false;
19977     
19978     if (typeof(depreciated_tpl) == 'undefined') {
19979         // new way.. - universal constructor.
19980         Roo.apply(this, config);
19981         this.el  = Roo.get(this.el);
19982     } else {
19983         // old format..
19984         this.el  = Roo.get(config);
19985         this.tpl = depreciated_tpl;
19986         Roo.apply(this, depreciated_config);
19987     }
19988     this.wrapEl  = this.el.wrap().wrap();
19989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19990     
19991     
19992     if(typeof(this.tpl) == "string"){
19993         this.tpl = new Roo.Template(this.tpl);
19994     } else {
19995         // support xtype ctors..
19996         this.tpl = new Roo.factory(this.tpl, Roo);
19997     }
19998     
19999     
20000     this.tpl.compile();
20001     
20002     /** @private */
20003     this.addEvents({
20004         /**
20005          * @event beforeclick
20006          * Fires before a click is processed. Returns false to cancel the default action.
20007          * @param {Roo.View} this
20008          * @param {Number} index The index of the target node
20009          * @param {HTMLElement} node The target node
20010          * @param {Roo.EventObject} e The raw event object
20011          */
20012             "beforeclick" : true,
20013         /**
20014          * @event click
20015          * Fires when a template node is clicked.
20016          * @param {Roo.View} this
20017          * @param {Number} index The index of the target node
20018          * @param {HTMLElement} node The target node
20019          * @param {Roo.EventObject} e The raw event object
20020          */
20021             "click" : true,
20022         /**
20023          * @event dblclick
20024          * Fires when a template node is double clicked.
20025          * @param {Roo.View} this
20026          * @param {Number} index The index of the target node
20027          * @param {HTMLElement} node The target node
20028          * @param {Roo.EventObject} e The raw event object
20029          */
20030             "dblclick" : true,
20031         /**
20032          * @event contextmenu
20033          * Fires when a template node is right clicked.
20034          * @param {Roo.View} this
20035          * @param {Number} index The index of the target node
20036          * @param {HTMLElement} node The target node
20037          * @param {Roo.EventObject} e The raw event object
20038          */
20039             "contextmenu" : true,
20040         /**
20041          * @event selectionchange
20042          * Fires when the selected nodes change.
20043          * @param {Roo.View} this
20044          * @param {Array} selections Array of the selected nodes
20045          */
20046             "selectionchange" : true,
20047     
20048         /**
20049          * @event beforeselect
20050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20051          * @param {Roo.View} this
20052          * @param {HTMLElement} node The node to be selected
20053          * @param {Array} selections Array of currently selected nodes
20054          */
20055             "beforeselect" : true,
20056         /**
20057          * @event preparedata
20058          * Fires on every row to render, to allow you to change the data.
20059          * @param {Roo.View} this
20060          * @param {Object} data to be rendered (change this)
20061          */
20062           "preparedata" : true
20063           
20064           
20065         });
20066
20067
20068
20069     this.el.on({
20070         "click": this.onClick,
20071         "dblclick": this.onDblClick,
20072         "contextmenu": this.onContextMenu,
20073         scope:this
20074     });
20075
20076     this.selections = [];
20077     this.nodes = [];
20078     this.cmp = new Roo.CompositeElementLite([]);
20079     if(this.store){
20080         this.store = Roo.factory(this.store, Roo.data);
20081         this.setStore(this.store, true);
20082     }
20083     
20084     if ( this.footer && this.footer.xtype) {
20085            
20086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20087         
20088         this.footer.dataSource = this.store;
20089         this.footer.container = fctr;
20090         this.footer = Roo.factory(this.footer, Roo);
20091         fctr.insertFirst(this.el);
20092         
20093         // this is a bit insane - as the paging toolbar seems to detach the el..
20094 //        dom.parentNode.parentNode.parentNode
20095          // they get detached?
20096     }
20097     
20098     
20099     Roo.View.superclass.constructor.call(this);
20100     
20101     
20102 };
20103
20104 Roo.extend(Roo.View, Roo.util.Observable, {
20105     
20106      /**
20107      * @cfg {Roo.data.Store} store Data store to load data from.
20108      */
20109     store : false,
20110     
20111     /**
20112      * @cfg {String|Roo.Element} el The container element.
20113      */
20114     el : '',
20115     
20116     /**
20117      * @cfg {String|Roo.Template} tpl The template used by this View 
20118      */
20119     tpl : false,
20120     /**
20121      * @cfg {String} dataName the named area of the template to use as the data area
20122      *                          Works with domtemplates roo-name="name"
20123      */
20124     dataName: false,
20125     /**
20126      * @cfg {String} selectedClass The css class to add to selected nodes
20127      */
20128     selectedClass : "x-view-selected",
20129      /**
20130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20131      */
20132     emptyText : "",
20133     
20134     /**
20135      * @cfg {String} text to display on mask (default Loading)
20136      */
20137     mask : false,
20138     /**
20139      * @cfg {Boolean} multiSelect Allow multiple selection
20140      */
20141     multiSelect : false,
20142     /**
20143      * @cfg {Boolean} singleSelect Allow single selection
20144      */
20145     singleSelect:  false,
20146     
20147     /**
20148      * @cfg {Boolean} toggleSelect - selecting 
20149      */
20150     toggleSelect : false,
20151     
20152     /**
20153      * @cfg {Boolean} tickable - selecting 
20154      */
20155     tickable : false,
20156     
20157     /**
20158      * Returns the element this view is bound to.
20159      * @return {Roo.Element}
20160      */
20161     getEl : function(){
20162         return this.wrapEl;
20163     },
20164     
20165     
20166
20167     /**
20168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20169      */
20170     refresh : function(){
20171         //Roo.log('refresh');
20172         var t = this.tpl;
20173         
20174         // if we are using something like 'domtemplate', then
20175         // the what gets used is:
20176         // t.applySubtemplate(NAME, data, wrapping data..)
20177         // the outer template then get' applied with
20178         //     the store 'extra data'
20179         // and the body get's added to the
20180         //      roo-name="data" node?
20181         //      <span class='roo-tpl-{name}'></span> ?????
20182         
20183         
20184         
20185         this.clearSelections();
20186         this.el.update("");
20187         var html = [];
20188         var records = this.store.getRange();
20189         if(records.length < 1) {
20190             
20191             // is this valid??  = should it render a template??
20192             
20193             this.el.update(this.emptyText);
20194             return;
20195         }
20196         var el = this.el;
20197         if (this.dataName) {
20198             this.el.update(t.apply(this.store.meta)); //????
20199             el = this.el.child('.roo-tpl-' + this.dataName);
20200         }
20201         
20202         for(var i = 0, len = records.length; i < len; i++){
20203             var data = this.prepareData(records[i].data, i, records[i]);
20204             this.fireEvent("preparedata", this, data, i, records[i]);
20205             
20206             var d = Roo.apply({}, data);
20207             
20208             if(this.tickable){
20209                 Roo.apply(d, {'roo-id' : Roo.id()});
20210                 
20211                 var _this = this;
20212             
20213                 Roo.each(this.parent.item, function(item){
20214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20215                         return;
20216                     }
20217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20218                 });
20219             }
20220             
20221             html[html.length] = Roo.util.Format.trim(
20222                 this.dataName ?
20223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20224                     t.apply(d)
20225             );
20226         }
20227         
20228         
20229         
20230         el.update(html.join(""));
20231         this.nodes = el.dom.childNodes;
20232         this.updateIndexes(0);
20233     },
20234     
20235
20236     /**
20237      * Function to override to reformat the data that is sent to
20238      * the template for each node.
20239      * DEPRICATED - use the preparedata event handler.
20240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20241      * a JSON object for an UpdateManager bound view).
20242      */
20243     prepareData : function(data, index, record)
20244     {
20245         this.fireEvent("preparedata", this, data, index, record);
20246         return data;
20247     },
20248
20249     onUpdate : function(ds, record){
20250         // Roo.log('on update');   
20251         this.clearSelections();
20252         var index = this.store.indexOf(record);
20253         var n = this.nodes[index];
20254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20255         n.parentNode.removeChild(n);
20256         this.updateIndexes(index, index);
20257     },
20258
20259     
20260     
20261 // --------- FIXME     
20262     onAdd : function(ds, records, index)
20263     {
20264         //Roo.log(['on Add', ds, records, index] );        
20265         this.clearSelections();
20266         if(this.nodes.length == 0){
20267             this.refresh();
20268             return;
20269         }
20270         var n = this.nodes[index];
20271         for(var i = 0, len = records.length; i < len; i++){
20272             var d = this.prepareData(records[i].data, i, records[i]);
20273             if(n){
20274                 this.tpl.insertBefore(n, d);
20275             }else{
20276                 
20277                 this.tpl.append(this.el, d);
20278             }
20279         }
20280         this.updateIndexes(index);
20281     },
20282
20283     onRemove : function(ds, record, index){
20284        // Roo.log('onRemove');
20285         this.clearSelections();
20286         var el = this.dataName  ?
20287             this.el.child('.roo-tpl-' + this.dataName) :
20288             this.el; 
20289         
20290         el.dom.removeChild(this.nodes[index]);
20291         this.updateIndexes(index);
20292     },
20293
20294     /**
20295      * Refresh an individual node.
20296      * @param {Number} index
20297      */
20298     refreshNode : function(index){
20299         this.onUpdate(this.store, this.store.getAt(index));
20300     },
20301
20302     updateIndexes : function(startIndex, endIndex){
20303         var ns = this.nodes;
20304         startIndex = startIndex || 0;
20305         endIndex = endIndex || ns.length - 1;
20306         for(var i = startIndex; i <= endIndex; i++){
20307             ns[i].nodeIndex = i;
20308         }
20309     },
20310
20311     /**
20312      * Changes the data store this view uses and refresh the view.
20313      * @param {Store} store
20314      */
20315     setStore : function(store, initial){
20316         if(!initial && this.store){
20317             this.store.un("datachanged", this.refresh);
20318             this.store.un("add", this.onAdd);
20319             this.store.un("remove", this.onRemove);
20320             this.store.un("update", this.onUpdate);
20321             this.store.un("clear", this.refresh);
20322             this.store.un("beforeload", this.onBeforeLoad);
20323             this.store.un("load", this.onLoad);
20324             this.store.un("loadexception", this.onLoad);
20325         }
20326         if(store){
20327           
20328             store.on("datachanged", this.refresh, this);
20329             store.on("add", this.onAdd, this);
20330             store.on("remove", this.onRemove, this);
20331             store.on("update", this.onUpdate, this);
20332             store.on("clear", this.refresh, this);
20333             store.on("beforeload", this.onBeforeLoad, this);
20334             store.on("load", this.onLoad, this);
20335             store.on("loadexception", this.onLoad, this);
20336         }
20337         
20338         if(store){
20339             this.refresh();
20340         }
20341     },
20342     /**
20343      * onbeforeLoad - masks the loading area.
20344      *
20345      */
20346     onBeforeLoad : function(store,opts)
20347     {
20348          //Roo.log('onBeforeLoad');   
20349         if (!opts.add) {
20350             this.el.update("");
20351         }
20352         this.el.mask(this.mask ? this.mask : "Loading" ); 
20353     },
20354     onLoad : function ()
20355     {
20356         this.el.unmask();
20357     },
20358     
20359
20360     /**
20361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20362      * @param {HTMLElement} node
20363      * @return {HTMLElement} The template node
20364      */
20365     findItemFromChild : function(node){
20366         var el = this.dataName  ?
20367             this.el.child('.roo-tpl-' + this.dataName,true) :
20368             this.el.dom; 
20369         
20370         if(!node || node.parentNode == el){
20371                     return node;
20372             }
20373             var p = node.parentNode;
20374             while(p && p != el){
20375             if(p.parentNode == el){
20376                 return p;
20377             }
20378             p = p.parentNode;
20379         }
20380             return null;
20381     },
20382
20383     /** @ignore */
20384     onClick : function(e){
20385         var item = this.findItemFromChild(e.getTarget());
20386         if(item){
20387             var index = this.indexOf(item);
20388             if(this.onItemClick(item, index, e) !== false){
20389                 this.fireEvent("click", this, index, item, e);
20390             }
20391         }else{
20392             this.clearSelections();
20393         }
20394     },
20395
20396     /** @ignore */
20397     onContextMenu : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20401         }
20402     },
20403
20404     /** @ignore */
20405     onDblClick : function(e){
20406         var item = this.findItemFromChild(e.getTarget());
20407         if(item){
20408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20409         }
20410     },
20411
20412     onItemClick : function(item, index, e)
20413     {
20414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20415             return false;
20416         }
20417         if (this.toggleSelect) {
20418             var m = this.isSelected(item) ? 'unselect' : 'select';
20419             //Roo.log(m);
20420             var _t = this;
20421             _t[m](item, true, false);
20422             return true;
20423         }
20424         if(this.multiSelect || this.singleSelect){
20425             if(this.multiSelect && e.shiftKey && this.lastSelection){
20426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20427             }else{
20428                 this.select(item, this.multiSelect && e.ctrlKey);
20429                 this.lastSelection = item;
20430             }
20431             
20432             if(!this.tickable){
20433                 e.preventDefault();
20434             }
20435             
20436         }
20437         return true;
20438     },
20439
20440     /**
20441      * Get the number of selected nodes.
20442      * @return {Number}
20443      */
20444     getSelectionCount : function(){
20445         return this.selections.length;
20446     },
20447
20448     /**
20449      * Get the currently selected nodes.
20450      * @return {Array} An array of HTMLElements
20451      */
20452     getSelectedNodes : function(){
20453         return this.selections;
20454     },
20455
20456     /**
20457      * Get the indexes of the selected nodes.
20458      * @return {Array}
20459      */
20460     getSelectedIndexes : function(){
20461         var indexes = [], s = this.selections;
20462         for(var i = 0, len = s.length; i < len; i++){
20463             indexes.push(s[i].nodeIndex);
20464         }
20465         return indexes;
20466     },
20467
20468     /**
20469      * Clear all selections
20470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20471      */
20472     clearSelections : function(suppressEvent){
20473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20474             this.cmp.elements = this.selections;
20475             this.cmp.removeClass(this.selectedClass);
20476             this.selections = [];
20477             if(!suppressEvent){
20478                 this.fireEvent("selectionchange", this, this.selections);
20479             }
20480         }
20481     },
20482
20483     /**
20484      * Returns true if the passed node is selected
20485      * @param {HTMLElement/Number} node The node or node index
20486      * @return {Boolean}
20487      */
20488     isSelected : function(node){
20489         var s = this.selections;
20490         if(s.length < 1){
20491             return false;
20492         }
20493         node = this.getNode(node);
20494         return s.indexOf(node) !== -1;
20495     },
20496
20497     /**
20498      * Selects nodes.
20499      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20500      * @param {Boolean} keepExisting (optional) true to keep existing selections
20501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20502      */
20503     select : function(nodeInfo, keepExisting, suppressEvent){
20504         if(nodeInfo instanceof Array){
20505             if(!keepExisting){
20506                 this.clearSelections(true);
20507             }
20508             for(var i = 0, len = nodeInfo.length; i < len; i++){
20509                 this.select(nodeInfo[i], true, true);
20510             }
20511             return;
20512         } 
20513         var node = this.getNode(nodeInfo);
20514         if(!node || this.isSelected(node)){
20515             return; // already selected.
20516         }
20517         if(!keepExisting){
20518             this.clearSelections(true);
20519         }
20520         
20521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20522             Roo.fly(node).addClass(this.selectedClass);
20523             this.selections.push(node);
20524             if(!suppressEvent){
20525                 this.fireEvent("selectionchange", this, this.selections);
20526             }
20527         }
20528         
20529         
20530     },
20531       /**
20532      * Unselects nodes.
20533      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20534      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20536      */
20537     unselect : function(nodeInfo, keepExisting, suppressEvent)
20538     {
20539         if(nodeInfo instanceof Array){
20540             Roo.each(this.selections, function(s) {
20541                 this.unselect(s, nodeInfo);
20542             }, this);
20543             return;
20544         }
20545         var node = this.getNode(nodeInfo);
20546         if(!node || !this.isSelected(node)){
20547             //Roo.log("not selected");
20548             return; // not selected.
20549         }
20550         // fireevent???
20551         var ns = [];
20552         Roo.each(this.selections, function(s) {
20553             if (s == node ) {
20554                 Roo.fly(node).removeClass(this.selectedClass);
20555
20556                 return;
20557             }
20558             ns.push(s);
20559         },this);
20560         
20561         this.selections= ns;
20562         this.fireEvent("selectionchange", this, this.selections);
20563     },
20564
20565     /**
20566      * Gets a template node.
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {HTMLElement} The node or null if it wasn't found
20569      */
20570     getNode : function(nodeInfo){
20571         if(typeof nodeInfo == "string"){
20572             return document.getElementById(nodeInfo);
20573         }else if(typeof nodeInfo == "number"){
20574             return this.nodes[nodeInfo];
20575         }
20576         return nodeInfo;
20577     },
20578
20579     /**
20580      * Gets a range template nodes.
20581      * @param {Number} startIndex
20582      * @param {Number} endIndex
20583      * @return {Array} An array of nodes
20584      */
20585     getNodes : function(start, end){
20586         var ns = this.nodes;
20587         start = start || 0;
20588         end = typeof end == "undefined" ? ns.length - 1 : end;
20589         var nodes = [];
20590         if(start <= end){
20591             for(var i = start; i <= end; i++){
20592                 nodes.push(ns[i]);
20593             }
20594         } else{
20595             for(var i = start; i >= end; i--){
20596                 nodes.push(ns[i]);
20597             }
20598         }
20599         return nodes;
20600     },
20601
20602     /**
20603      * Finds the index of the passed node
20604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605      * @return {Number} The index of the node or -1
20606      */
20607     indexOf : function(node){
20608         node = this.getNode(node);
20609         if(typeof node.nodeIndex == "number"){
20610             return node.nodeIndex;
20611         }
20612         var ns = this.nodes;
20613         for(var i = 0, len = ns.length; i < len; i++){
20614             if(ns[i] == node){
20615                 return i;
20616             }
20617         }
20618         return -1;
20619     }
20620 });
20621 /*
20622  * - LGPL
20623  *
20624  * based on jquery fullcalendar
20625  * 
20626  */
20627
20628 Roo.bootstrap = Roo.bootstrap || {};
20629 /**
20630  * @class Roo.bootstrap.Calendar
20631  * @extends Roo.bootstrap.Component
20632  * Bootstrap Calendar class
20633  * @cfg {Boolean} loadMask (true|false) default false
20634  * @cfg {Object} header generate the user specific header of the calendar, default false
20635
20636  * @constructor
20637  * Create a new Container
20638  * @param {Object} config The config object
20639  */
20640
20641
20642
20643 Roo.bootstrap.Calendar = function(config){
20644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20645      this.addEvents({
20646         /**
20647              * @event select
20648              * Fires when a date is selected
20649              * @param {DatePicker} this
20650              * @param {Date} date The selected date
20651              */
20652         'select': true,
20653         /**
20654              * @event monthchange
20655              * Fires when the displayed month changes 
20656              * @param {DatePicker} this
20657              * @param {Date} date The selected month
20658              */
20659         'monthchange': true,
20660         /**
20661              * @event evententer
20662              * Fires when mouse over an event
20663              * @param {Calendar} this
20664              * @param {event} Event
20665              */
20666         'evententer': true,
20667         /**
20668              * @event eventleave
20669              * Fires when the mouse leaves an
20670              * @param {Calendar} this
20671              * @param {event}
20672              */
20673         'eventleave': true,
20674         /**
20675              * @event eventclick
20676              * Fires when the mouse click an
20677              * @param {Calendar} this
20678              * @param {event}
20679              */
20680         'eventclick': true
20681         
20682     });
20683
20684 };
20685
20686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20687     
20688           /**
20689      * @cfg {Roo.data.Store} store
20690      * The data source for the calendar
20691      */
20692         store : false,
20693      /**
20694      * @cfg {Number} startDay
20695      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20696      */
20697     startDay : 0,
20698     
20699     loadMask : false,
20700     
20701     header : false,
20702       
20703     getAutoCreate : function(){
20704         
20705         
20706         var fc_button = function(name, corner, style, content ) {
20707             return Roo.apply({},{
20708                 tag : 'span',
20709                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20710                          (corner.length ?
20711                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20712                             ''
20713                         ),
20714                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20715                 unselectable: 'on'
20716             });
20717         };
20718         
20719         var header = {};
20720         
20721         if(!this.header){
20722             header = {
20723                 tag : 'table',
20724                 cls : 'fc-header',
20725                 style : 'width:100%',
20726                 cn : [
20727                     {
20728                         tag: 'tr',
20729                         cn : [
20730                             {
20731                                 tag : 'td',
20732                                 cls : 'fc-header-left',
20733                                 cn : [
20734                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20735                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20736                                     { tag: 'span', cls: 'fc-header-space' },
20737                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20738
20739
20740                                 ]
20741                             },
20742
20743                             {
20744                                 tag : 'td',
20745                                 cls : 'fc-header-center',
20746                                 cn : [
20747                                     {
20748                                         tag: 'span',
20749                                         cls: 'fc-header-title',
20750                                         cn : {
20751                                             tag: 'H2',
20752                                             html : 'month / year'
20753                                         }
20754                                     }
20755
20756                                 ]
20757                             },
20758                             {
20759                                 tag : 'td',
20760                                 cls : 'fc-header-right',
20761                                 cn : [
20762                               /*      fc_button('month', 'left', '', 'month' ),
20763                                     fc_button('week', '', '', 'week' ),
20764                                     fc_button('day', 'right', '', 'day' )
20765                                 */    
20766
20767                                 ]
20768                             }
20769
20770                         ]
20771                     }
20772                 ]
20773             };
20774         }
20775         
20776         header = this.header;
20777         
20778        
20779         var cal_heads = function() {
20780             var ret = [];
20781             // fixme - handle this.
20782             
20783             for (var i =0; i < Date.dayNames.length; i++) {
20784                 var d = Date.dayNames[i];
20785                 ret.push({
20786                     tag: 'th',
20787                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20788                     html : d.substring(0,3)
20789                 });
20790                 
20791             }
20792             ret[0].cls += ' fc-first';
20793             ret[6].cls += ' fc-last';
20794             return ret;
20795         };
20796         var cal_cell = function(n) {
20797             return  {
20798                 tag: 'td',
20799                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20800                 cn : [
20801                     {
20802                         cn : [
20803                             {
20804                                 cls: 'fc-day-number',
20805                                 html: 'D'
20806                             },
20807                             {
20808                                 cls: 'fc-day-content',
20809                              
20810                                 cn : [
20811                                      {
20812                                         style: 'position: relative;' // height: 17px;
20813                                     }
20814                                 ]
20815                             }
20816                             
20817                             
20818                         ]
20819                     }
20820                 ]
20821                 
20822             }
20823         };
20824         var cal_rows = function() {
20825             
20826             var ret = [];
20827             for (var r = 0; r < 6; r++) {
20828                 var row= {
20829                     tag : 'tr',
20830                     cls : 'fc-week',
20831                     cn : []
20832                 };
20833                 
20834                 for (var i =0; i < Date.dayNames.length; i++) {
20835                     var d = Date.dayNames[i];
20836                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20837
20838                 }
20839                 row.cn[0].cls+=' fc-first';
20840                 row.cn[0].cn[0].style = 'min-height:90px';
20841                 row.cn[6].cls+=' fc-last';
20842                 ret.push(row);
20843                 
20844             }
20845             ret[0].cls += ' fc-first';
20846             ret[4].cls += ' fc-prev-last';
20847             ret[5].cls += ' fc-last';
20848             return ret;
20849             
20850         };
20851         
20852         var cal_table = {
20853             tag: 'table',
20854             cls: 'fc-border-separate',
20855             style : 'width:100%',
20856             cellspacing  : 0,
20857             cn : [
20858                 { 
20859                     tag: 'thead',
20860                     cn : [
20861                         { 
20862                             tag: 'tr',
20863                             cls : 'fc-first fc-last',
20864                             cn : cal_heads()
20865                         }
20866                     ]
20867                 },
20868                 { 
20869                     tag: 'tbody',
20870                     cn : cal_rows()
20871                 }
20872                   
20873             ]
20874         };
20875          
20876          var cfg = {
20877             cls : 'fc fc-ltr',
20878             cn : [
20879                 header,
20880                 {
20881                     cls : 'fc-content',
20882                     style : "position: relative;",
20883                     cn : [
20884                         {
20885                             cls : 'fc-view fc-view-month fc-grid',
20886                             style : 'position: relative',
20887                             unselectable : 'on',
20888                             cn : [
20889                                 {
20890                                     cls : 'fc-event-container',
20891                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20892                                 },
20893                                 cal_table
20894                             ]
20895                         }
20896                     ]
20897     
20898                 }
20899            ] 
20900             
20901         };
20902         
20903          
20904         
20905         return cfg;
20906     },
20907     
20908     
20909     initEvents : function()
20910     {
20911         if(!this.store){
20912             throw "can not find store for calendar";
20913         }
20914         
20915         var mark = {
20916             tag: "div",
20917             cls:"x-dlg-mask",
20918             style: "text-align:center",
20919             cn: [
20920                 {
20921                     tag: "div",
20922                     style: "background-color:white;width:50%;margin:250 auto",
20923                     cn: [
20924                         {
20925                             tag: "img",
20926                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20927                         },
20928                         {
20929                             tag: "span",
20930                             html: "Loading"
20931                         }
20932                         
20933                     ]
20934                 }
20935             ]
20936         };
20937         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20938         
20939         var size = this.el.select('.fc-content', true).first().getSize();
20940         this.maskEl.setSize(size.width, size.height);
20941         this.maskEl.enableDisplayMode("block");
20942         if(!this.loadMask){
20943             this.maskEl.hide();
20944         }
20945         
20946         this.store = Roo.factory(this.store, Roo.data);
20947         this.store.on('load', this.onLoad, this);
20948         this.store.on('beforeload', this.onBeforeLoad, this);
20949         
20950         this.resize();
20951         
20952         this.cells = this.el.select('.fc-day',true);
20953         //Roo.log(this.cells);
20954         this.textNodes = this.el.query('.fc-day-number');
20955         this.cells.addClassOnOver('fc-state-hover');
20956         
20957         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20958         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20959         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20960         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20961         
20962         this.on('monthchange', this.onMonthChange, this);
20963         
20964         this.update(new Date().clearTime());
20965     },
20966     
20967     resize : function() {
20968         var sz  = this.el.getSize();
20969         
20970         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20971         this.el.select('.fc-day-content div',true).setHeight(34);
20972     },
20973     
20974     
20975     // private
20976     showPrevMonth : function(e){
20977         this.update(this.activeDate.add("mo", -1));
20978     },
20979     showToday : function(e){
20980         this.update(new Date().clearTime());
20981     },
20982     // private
20983     showNextMonth : function(e){
20984         this.update(this.activeDate.add("mo", 1));
20985     },
20986
20987     // private
20988     showPrevYear : function(){
20989         this.update(this.activeDate.add("y", -1));
20990     },
20991
20992     // private
20993     showNextYear : function(){
20994         this.update(this.activeDate.add("y", 1));
20995     },
20996
20997     
20998    // private
20999     update : function(date)
21000     {
21001         var vd = this.activeDate;
21002         this.activeDate = date;
21003 //        if(vd && this.el){
21004 //            var t = date.getTime();
21005 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21006 //                Roo.log('using add remove');
21007 //                
21008 //                this.fireEvent('monthchange', this, date);
21009 //                
21010 //                this.cells.removeClass("fc-state-highlight");
21011 //                this.cells.each(function(c){
21012 //                   if(c.dateValue == t){
21013 //                       c.addClass("fc-state-highlight");
21014 //                       setTimeout(function(){
21015 //                            try{c.dom.firstChild.focus();}catch(e){}
21016 //                       }, 50);
21017 //                       return false;
21018 //                   }
21019 //                   return true;
21020 //                });
21021 //                return;
21022 //            }
21023 //        }
21024         
21025         var days = date.getDaysInMonth();
21026         
21027         var firstOfMonth = date.getFirstDateOfMonth();
21028         var startingPos = firstOfMonth.getDay()-this.startDay;
21029         
21030         if(startingPos < this.startDay){
21031             startingPos += 7;
21032         }
21033         
21034         var pm = date.add(Date.MONTH, -1);
21035         var prevStart = pm.getDaysInMonth()-startingPos;
21036 //        
21037         this.cells = this.el.select('.fc-day',true);
21038         this.textNodes = this.el.query('.fc-day-number');
21039         this.cells.addClassOnOver('fc-state-hover');
21040         
21041         var cells = this.cells.elements;
21042         var textEls = this.textNodes;
21043         
21044         Roo.each(cells, function(cell){
21045             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21046         });
21047         
21048         days += startingPos;
21049
21050         // convert everything to numbers so it's fast
21051         var day = 86400000;
21052         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21053         //Roo.log(d);
21054         //Roo.log(pm);
21055         //Roo.log(prevStart);
21056         
21057         var today = new Date().clearTime().getTime();
21058         var sel = date.clearTime().getTime();
21059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21061         var ddMatch = this.disabledDatesRE;
21062         var ddText = this.disabledDatesText;
21063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21064         var ddaysText = this.disabledDaysText;
21065         var format = this.format;
21066         
21067         var setCellClass = function(cal, cell){
21068             cell.row = 0;
21069             cell.events = [];
21070             cell.more = [];
21071             //Roo.log('set Cell Class');
21072             cell.title = "";
21073             var t = d.getTime();
21074             
21075             //Roo.log(d);
21076             
21077             cell.dateValue = t;
21078             if(t == today){
21079                 cell.className += " fc-today";
21080                 cell.className += " fc-state-highlight";
21081                 cell.title = cal.todayText;
21082             }
21083             if(t == sel){
21084                 // disable highlight in other month..
21085                 //cell.className += " fc-state-highlight";
21086                 
21087             }
21088             // disabling
21089             if(t < min) {
21090                 cell.className = " fc-state-disabled";
21091                 cell.title = cal.minText;
21092                 return;
21093             }
21094             if(t > max) {
21095                 cell.className = " fc-state-disabled";
21096                 cell.title = cal.maxText;
21097                 return;
21098             }
21099             if(ddays){
21100                 if(ddays.indexOf(d.getDay()) != -1){
21101                     cell.title = ddaysText;
21102                     cell.className = " fc-state-disabled";
21103                 }
21104             }
21105             if(ddMatch && format){
21106                 var fvalue = d.dateFormat(format);
21107                 if(ddMatch.test(fvalue)){
21108                     cell.title = ddText.replace("%0", fvalue);
21109                     cell.className = " fc-state-disabled";
21110                 }
21111             }
21112             
21113             if (!cell.initialClassName) {
21114                 cell.initialClassName = cell.dom.className;
21115             }
21116             
21117             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21118         };
21119
21120         var i = 0;
21121         
21122         for(; i < startingPos; i++) {
21123             textEls[i].innerHTML = (++prevStart);
21124             d.setDate(d.getDate()+1);
21125             
21126             cells[i].className = "fc-past fc-other-month";
21127             setCellClass(this, cells[i]);
21128         }
21129         
21130         var intDay = 0;
21131         
21132         for(; i < days; i++){
21133             intDay = i - startingPos + 1;
21134             textEls[i].innerHTML = (intDay);
21135             d.setDate(d.getDate()+1);
21136             
21137             cells[i].className = ''; // "x-date-active";
21138             setCellClass(this, cells[i]);
21139         }
21140         var extraDays = 0;
21141         
21142         for(; i < 42; i++) {
21143             textEls[i].innerHTML = (++extraDays);
21144             d.setDate(d.getDate()+1);
21145             
21146             cells[i].className = "fc-future fc-other-month";
21147             setCellClass(this, cells[i]);
21148         }
21149         
21150         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21151         
21152         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21153         
21154         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21155         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21156         
21157         if(totalRows != 6){
21158             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21159             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21160         }
21161         
21162         this.fireEvent('monthchange', this, date);
21163         
21164         
21165         /*
21166         if(!this.internalRender){
21167             var main = this.el.dom.firstChild;
21168             var w = main.offsetWidth;
21169             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21170             Roo.fly(main).setWidth(w);
21171             this.internalRender = true;
21172             // opera does not respect the auto grow header center column
21173             // then, after it gets a width opera refuses to recalculate
21174             // without a second pass
21175             if(Roo.isOpera && !this.secondPass){
21176                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21177                 this.secondPass = true;
21178                 this.update.defer(10, this, [date]);
21179             }
21180         }
21181         */
21182         
21183     },
21184     
21185     findCell : function(dt) {
21186         dt = dt.clearTime().getTime();
21187         var ret = false;
21188         this.cells.each(function(c){
21189             //Roo.log("check " +c.dateValue + '?=' + dt);
21190             if(c.dateValue == dt){
21191                 ret = c;
21192                 return false;
21193             }
21194             return true;
21195         });
21196         
21197         return ret;
21198     },
21199     
21200     findCells : function(ev) {
21201         var s = ev.start.clone().clearTime().getTime();
21202        // Roo.log(s);
21203         var e= ev.end.clone().clearTime().getTime();
21204        // Roo.log(e);
21205         var ret = [];
21206         this.cells.each(function(c){
21207              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21208             
21209             if(c.dateValue > e){
21210                 return ;
21211             }
21212             if(c.dateValue < s){
21213                 return ;
21214             }
21215             ret.push(c);
21216         });
21217         
21218         return ret;    
21219     },
21220     
21221 //    findBestRow: function(cells)
21222 //    {
21223 //        var ret = 0;
21224 //        
21225 //        for (var i =0 ; i < cells.length;i++) {
21226 //            ret  = Math.max(cells[i].rows || 0,ret);
21227 //        }
21228 //        return ret;
21229 //        
21230 //    },
21231     
21232     
21233     addItem : function(ev)
21234     {
21235         // look for vertical location slot in
21236         var cells = this.findCells(ev);
21237         
21238 //        ev.row = this.findBestRow(cells);
21239         
21240         // work out the location.
21241         
21242         var crow = false;
21243         var rows = [];
21244         for(var i =0; i < cells.length; i++) {
21245             
21246             cells[i].row = cells[0].row;
21247             
21248             if(i == 0){
21249                 cells[i].row = cells[i].row + 1;
21250             }
21251             
21252             if (!crow) {
21253                 crow = {
21254                     start : cells[i],
21255                     end :  cells[i]
21256                 };
21257                 continue;
21258             }
21259             if (crow.start.getY() == cells[i].getY()) {
21260                 // on same row.
21261                 crow.end = cells[i];
21262                 continue;
21263             }
21264             // different row.
21265             rows.push(crow);
21266             crow = {
21267                 start: cells[i],
21268                 end : cells[i]
21269             };
21270             
21271         }
21272         
21273         rows.push(crow);
21274         ev.els = [];
21275         ev.rows = rows;
21276         ev.cells = cells;
21277         
21278         cells[0].events.push(ev);
21279         
21280         this.calevents.push(ev);
21281     },
21282     
21283     clearEvents: function() {
21284         
21285         if(!this.calevents){
21286             return;
21287         }
21288         
21289         Roo.each(this.cells.elements, function(c){
21290             c.row = 0;
21291             c.events = [];
21292             c.more = [];
21293         });
21294         
21295         Roo.each(this.calevents, function(e) {
21296             Roo.each(e.els, function(el) {
21297                 el.un('mouseenter' ,this.onEventEnter, this);
21298                 el.un('mouseleave' ,this.onEventLeave, this);
21299                 el.remove();
21300             },this);
21301         },this);
21302         
21303         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21304             e.remove();
21305         });
21306         
21307     },
21308     
21309     renderEvents: function()
21310     {   
21311         var _this = this;
21312         
21313         this.cells.each(function(c) {
21314             
21315             if(c.row < 5){
21316                 return;
21317             }
21318             
21319             var ev = c.events;
21320             
21321             var r = 4;
21322             if(c.row != c.events.length){
21323                 r = 4 - (4 - (c.row - c.events.length));
21324             }
21325             
21326             c.events = ev.slice(0, r);
21327             c.more = ev.slice(r);
21328             
21329             if(c.more.length && c.more.length == 1){
21330                 c.events.push(c.more.pop());
21331             }
21332             
21333             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21334             
21335         });
21336             
21337         this.cells.each(function(c) {
21338             
21339             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21340             
21341             
21342             for (var e = 0; e < c.events.length; e++){
21343                 var ev = c.events[e];
21344                 var rows = ev.rows;
21345                 
21346                 for(var i = 0; i < rows.length; i++) {
21347                 
21348                     // how many rows should it span..
21349
21350                     var  cfg = {
21351                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21352                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21353
21354                         unselectable : "on",
21355                         cn : [
21356                             {
21357                                 cls: 'fc-event-inner',
21358                                 cn : [
21359     //                                {
21360     //                                  tag:'span',
21361     //                                  cls: 'fc-event-time',
21362     //                                  html : cells.length > 1 ? '' : ev.time
21363     //                                },
21364                                     {
21365                                       tag:'span',
21366                                       cls: 'fc-event-title',
21367                                       html : String.format('{0}', ev.title)
21368                                     }
21369
21370
21371                                 ]
21372                             },
21373                             {
21374                                 cls: 'ui-resizable-handle ui-resizable-e',
21375                                 html : '&nbsp;&nbsp;&nbsp'
21376                             }
21377
21378                         ]
21379                     };
21380
21381                     if (i == 0) {
21382                         cfg.cls += ' fc-event-start';
21383                     }
21384                     if ((i+1) == rows.length) {
21385                         cfg.cls += ' fc-event-end';
21386                     }
21387
21388                     var ctr = _this.el.select('.fc-event-container',true).first();
21389                     var cg = ctr.createChild(cfg);
21390
21391                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21392                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21393
21394                     var r = (c.more.length) ? 1 : 0;
21395                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21396                     cg.setWidth(ebox.right - sbox.x -2);
21397
21398                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21399                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21400                     cg.on('click', _this.onEventClick, _this, ev);
21401
21402                     ev.els.push(cg);
21403                     
21404                 }
21405                 
21406             }
21407             
21408             
21409             if(c.more.length){
21410                 var  cfg = {
21411                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21412                     style : 'position: absolute',
21413                     unselectable : "on",
21414                     cn : [
21415                         {
21416                             cls: 'fc-event-inner',
21417                             cn : [
21418                                 {
21419                                   tag:'span',
21420                                   cls: 'fc-event-title',
21421                                   html : 'More'
21422                                 }
21423
21424
21425                             ]
21426                         },
21427                         {
21428                             cls: 'ui-resizable-handle ui-resizable-e',
21429                             html : '&nbsp;&nbsp;&nbsp'
21430                         }
21431
21432                     ]
21433                 };
21434
21435                 var ctr = _this.el.select('.fc-event-container',true).first();
21436                 var cg = ctr.createChild(cfg);
21437
21438                 var sbox = c.select('.fc-day-content',true).first().getBox();
21439                 var ebox = c.select('.fc-day-content',true).first().getBox();
21440                 //Roo.log(cg);
21441                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21442                 cg.setWidth(ebox.right - sbox.x -2);
21443
21444                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21445                 
21446             }
21447             
21448         });
21449         
21450         
21451         
21452     },
21453     
21454     onEventEnter: function (e, el,event,d) {
21455         this.fireEvent('evententer', this, el, event);
21456     },
21457     
21458     onEventLeave: function (e, el,event,d) {
21459         this.fireEvent('eventleave', this, el, event);
21460     },
21461     
21462     onEventClick: function (e, el,event,d) {
21463         this.fireEvent('eventclick', this, el, event);
21464     },
21465     
21466     onMonthChange: function () {
21467         this.store.load();
21468     },
21469     
21470     onMoreEventClick: function(e, el, more)
21471     {
21472         var _this = this;
21473         
21474         this.calpopover.placement = 'right';
21475         this.calpopover.setTitle('More');
21476         
21477         this.calpopover.setContent('');
21478         
21479         var ctr = this.calpopover.el.select('.popover-content', true).first();
21480         
21481         Roo.each(more, function(m){
21482             var cfg = {
21483                 cls : 'fc-event-hori fc-event-draggable',
21484                 html : m.title
21485             };
21486             var cg = ctr.createChild(cfg);
21487             
21488             cg.on('click', _this.onEventClick, _this, m);
21489         });
21490         
21491         this.calpopover.show(el);
21492         
21493         
21494     },
21495     
21496     onLoad: function () 
21497     {   
21498         this.calevents = [];
21499         var cal = this;
21500         
21501         if(this.store.getCount() > 0){
21502             this.store.data.each(function(d){
21503                cal.addItem({
21504                     id : d.data.id,
21505                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21506                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21507                     time : d.data.start_time,
21508                     title : d.data.title,
21509                     description : d.data.description,
21510                     venue : d.data.venue
21511                 });
21512             });
21513         }
21514         
21515         this.renderEvents();
21516         
21517         if(this.calevents.length && this.loadMask){
21518             this.maskEl.hide();
21519         }
21520     },
21521     
21522     onBeforeLoad: function()
21523     {
21524         this.clearEvents();
21525         if(this.loadMask){
21526             this.maskEl.show();
21527         }
21528     }
21529 });
21530
21531  
21532  /*
21533  * - LGPL
21534  *
21535  * element
21536  * 
21537  */
21538
21539 /**
21540  * @class Roo.bootstrap.Popover
21541  * @extends Roo.bootstrap.Component
21542  * @parent none builder
21543  * @children Roo.bootstrap.Component
21544  * Bootstrap Popover class
21545  * @cfg {String} html contents of the popover   (or false to use children..)
21546  * @cfg {String} title of popover (or false to hide)
21547  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21548  * @cfg {String} trigger click || hover (or false to trigger manually)
21549  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21550  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21551  *      - if false and it has a 'parent' then it will be automatically added to that element
21552  *      - if string - Roo.get  will be called 
21553  * @cfg {Number} delay - delay before showing
21554  
21555  * @constructor
21556  * Create a new Popover
21557  * @param {Object} config The config object
21558  */
21559
21560 Roo.bootstrap.Popover = function(config){
21561     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21562     
21563     this.addEvents({
21564         // raw events
21565          /**
21566          * @event show
21567          * After the popover show
21568          * 
21569          * @param {Roo.bootstrap.Popover} this
21570          */
21571         "show" : true,
21572         /**
21573          * @event hide
21574          * After the popover hide
21575          * 
21576          * @param {Roo.bootstrap.Popover} this
21577          */
21578         "hide" : true
21579     });
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21583     
21584     title: false,
21585     html: false,
21586     
21587     placement : 'right',
21588     trigger : 'hover', // hover
21589     modal : false,
21590     delay : 0,
21591     
21592     over: false,
21593     
21594     can_build_overlaid : false,
21595     
21596     maskEl : false, // the mask element
21597     headerEl : false,
21598     contentEl : false,
21599     alignEl : false, // when show is called with an element - this get's stored.
21600     
21601     getChildContainer : function()
21602     {
21603         return this.contentEl;
21604         
21605     },
21606     getPopoverHeader : function()
21607     {
21608         this.title = true; // flag not to hide it..
21609         this.headerEl.addClass('p-0');
21610         return this.headerEl
21611     },
21612     
21613     
21614     getAutoCreate : function(){
21615          
21616         var cfg = {
21617            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21618            style: 'display:block',
21619            cn : [
21620                 {
21621                     cls : 'arrow'
21622                 },
21623                 {
21624                     cls : 'popover-inner ',
21625                     cn : [
21626                         {
21627                             tag: 'h3',
21628                             cls: 'popover-title popover-header',
21629                             html : this.title === false ? '' : this.title
21630                         },
21631                         {
21632                             cls : 'popover-content popover-body '  + (this.cls || ''),
21633                             html : this.html || ''
21634                         }
21635                     ]
21636                     
21637                 }
21638            ]
21639         };
21640         
21641         return cfg;
21642     },
21643     /**
21644      * @param {string} the title
21645      */
21646     setTitle: function(str)
21647     {
21648         this.title = str;
21649         if (this.el) {
21650             this.headerEl.dom.innerHTML = str;
21651         }
21652         
21653     },
21654     /**
21655      * @param {string} the body content
21656      */
21657     setContent: function(str)
21658     {
21659         this.html = str;
21660         if (this.contentEl) {
21661             this.contentEl.dom.innerHTML = str;
21662         }
21663         
21664     },
21665     // as it get's added to the bottom of the page.
21666     onRender : function(ct, position)
21667     {
21668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21669         
21670         
21671         
21672         if(!this.el){
21673             var cfg = Roo.apply({},  this.getAutoCreate());
21674             cfg.id = Roo.id();
21675             
21676             if (this.cls) {
21677                 cfg.cls += ' ' + this.cls;
21678             }
21679             if (this.style) {
21680                 cfg.style = this.style;
21681             }
21682             //Roo.log("adding to ");
21683             this.el = Roo.get(document.body).createChild(cfg, position);
21684 //            Roo.log(this.el);
21685         }
21686         
21687         this.contentEl = this.el.select('.popover-content',true).first();
21688         this.headerEl =  this.el.select('.popover-title',true).first();
21689         
21690         var nitems = [];
21691         if(typeof(this.items) != 'undefined'){
21692             var items = this.items;
21693             delete this.items;
21694
21695             for(var i =0;i < items.length;i++) {
21696                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21697             }
21698         }
21699
21700         this.items = nitems;
21701         
21702         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21703         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21704         
21705         
21706         
21707         this.initEvents();
21708     },
21709     
21710     resizeMask : function()
21711     {
21712         this.maskEl.setSize(
21713             Roo.lib.Dom.getViewWidth(true),
21714             Roo.lib.Dom.getViewHeight(true)
21715         );
21716     },
21717     
21718     initEvents : function()
21719     {
21720         
21721         if (!this.modal) { 
21722             Roo.bootstrap.Popover.register(this);
21723         }
21724          
21725         this.arrowEl = this.el.select('.arrow',true).first();
21726         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21727         this.el.enableDisplayMode('block');
21728         this.el.hide();
21729  
21730         
21731         if (this.over === false && !this.parent()) {
21732             return; 
21733         }
21734         if (this.triggers === false) {
21735             return;
21736         }
21737          
21738         // support parent
21739         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21740         var triggers = this.trigger ? this.trigger.split(' ') : [];
21741         Roo.each(triggers, function(trigger) {
21742         
21743             if (trigger == 'click') {
21744                 on_el.on('click', this.toggle, this);
21745             } else if (trigger != 'manual') {
21746                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21747                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21748       
21749                 on_el.on(eventIn  ,this.enter, this);
21750                 on_el.on(eventOut, this.leave, this);
21751             }
21752         }, this);
21753     },
21754     
21755     
21756     // private
21757     timeout : null,
21758     hoverState : null,
21759     
21760     toggle : function () {
21761         this.hoverState == 'in' ? this.leave() : this.enter();
21762     },
21763     
21764     enter : function () {
21765         
21766         clearTimeout(this.timeout);
21767     
21768         this.hoverState = 'in';
21769     
21770         if (!this.delay || !this.delay.show) {
21771             this.show();
21772             return;
21773         }
21774         var _t = this;
21775         this.timeout = setTimeout(function () {
21776             if (_t.hoverState == 'in') {
21777                 _t.show();
21778             }
21779         }, this.delay.show)
21780     },
21781     
21782     leave : function() {
21783         clearTimeout(this.timeout);
21784     
21785         this.hoverState = 'out';
21786     
21787         if (!this.delay || !this.delay.hide) {
21788             this.hide();
21789             return;
21790         }
21791         var _t = this;
21792         this.timeout = setTimeout(function () {
21793             if (_t.hoverState == 'out') {
21794                 _t.hide();
21795             }
21796         }, this.delay.hide)
21797     },
21798     
21799     /**
21800      * update the position of the dialog
21801      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21802      * 
21803      *
21804      */
21805     
21806     doAlign : function()
21807     {
21808         
21809         if (this.alignEl) {
21810             this.updatePosition(this.placement, true);
21811              
21812         } else {
21813             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21814             var es = this.el.getSize();
21815             var x = Roo.lib.Dom.getViewWidth()/2;
21816             var y = Roo.lib.Dom.getViewHeight()/2;
21817             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21818             
21819         }
21820
21821          
21822          
21823         
21824         
21825     },
21826     
21827     /**
21828      * Show the popover
21829      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21830      * @param {string} (left|right|top|bottom) position
21831      */
21832     show : function (on_el, placement)
21833     {
21834         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21835         on_el = on_el || false; // default to false
21836          
21837         if (!on_el) {
21838             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21839                 on_el = this.parent().el;
21840             } else if (this.over) {
21841                 on_el = Roo.get(this.over);
21842             }
21843             
21844         }
21845         
21846         this.alignEl = Roo.get( on_el );
21847
21848         if (!this.el) {
21849             this.render(document.body);
21850         }
21851         
21852         
21853          
21854         
21855         if (this.title === false) {
21856             this.headerEl.hide();
21857         }
21858         
21859        
21860         this.el.show();
21861         this.el.dom.style.display = 'block';
21862          
21863         this.doAlign();
21864         
21865         //var arrow = this.el.select('.arrow',true).first();
21866         //arrow.set(align[2], 
21867         
21868         this.el.addClass('in');
21869         
21870          
21871         
21872         this.hoverState = 'in';
21873         
21874         if (this.modal) {
21875             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21876             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877             this.maskEl.dom.style.display = 'block';
21878             this.maskEl.addClass('show');
21879         }
21880         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21881  
21882         this.fireEvent('show', this);
21883         
21884     },
21885     /**
21886      * fire this manually after loading a grid in the table for example
21887      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21888      * @param {Boolean} try and move it if we cant get right position.
21889      */
21890     updatePosition : function(placement, try_move)
21891     {
21892         // allow for calling with no parameters
21893         placement = placement   ? placement :  this.placement;
21894         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21895         
21896         this.el.removeClass([
21897             'fade','top','bottom', 'left', 'right','in',
21898             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21899         ]);
21900         this.el.addClass(placement + ' bs-popover-' + placement);
21901         
21902         if (!this.alignEl ) {
21903             return false;
21904         }
21905         
21906         switch (placement) {
21907             case 'right':
21908                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21909                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21910                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21911                     //normal display... or moved up/down.
21912                     this.el.setXY(offset);
21913                     var xy = this.alignEl.getAnchorXY('tr', false);
21914                     xy[0]+=2;xy[1]+=5;
21915                     this.arrowEl.setXY(xy);
21916                     return true;
21917                 }
21918                 // continue through...
21919                 return this.updatePosition('left', false);
21920                 
21921             
21922             case 'left':
21923                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21924                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21925                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21926                     //normal display... or moved up/down.
21927                     this.el.setXY(offset);
21928                     var xy = this.alignEl.getAnchorXY('tl', false);
21929                     xy[0]-=10;xy[1]+=5; // << fix me
21930                     this.arrowEl.setXY(xy);
21931                     return true;
21932                 }
21933                 // call self...
21934                 return this.updatePosition('right', false);
21935             
21936             case 'top':
21937                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21938                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21939                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21940                     //normal display... or moved up/down.
21941                     this.el.setXY(offset);
21942                     var xy = this.alignEl.getAnchorXY('t', false);
21943                     xy[1]-=10; // << fix me
21944                     this.arrowEl.setXY(xy);
21945                     return true;
21946                 }
21947                 // fall through
21948                return this.updatePosition('bottom', false);
21949             
21950             case 'bottom':
21951                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21952                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21953                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21954                     //normal display... or moved up/down.
21955                     this.el.setXY(offset);
21956                     var xy = this.alignEl.getAnchorXY('b', false);
21957                      xy[1]+=2; // << fix me
21958                     this.arrowEl.setXY(xy);
21959                     return true;
21960                 }
21961                 // fall through
21962                 return this.updatePosition('top', false);
21963                 
21964             
21965         }
21966         
21967         
21968         return false;
21969     },
21970     
21971     hide : function()
21972     {
21973         this.el.setXY([0,0]);
21974         this.el.removeClass('in');
21975         this.el.hide();
21976         this.hoverState = null;
21977         this.maskEl.hide(); // always..
21978         this.fireEvent('hide', this);
21979     }
21980     
21981 });
21982
21983
21984 Roo.apply(Roo.bootstrap.Popover, {
21985
21986     alignment : {
21987         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21988         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21989         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21990         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21991     },
21992     
21993     zIndex : 20001,
21994
21995     clickHander : false,
21996     
21997     
21998
21999     onMouseDown : function(e)
22000     {
22001         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22002             /// what is nothing is showing..
22003             this.hideAll();
22004         }
22005          
22006     },
22007     
22008     
22009     popups : [],
22010     
22011     register : function(popup)
22012     {
22013         if (!Roo.bootstrap.Popover.clickHandler) {
22014             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22015         }
22016         // hide other popups.
22017         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22018         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22019         this.hideAll(); //<< why?
22020         //this.popups.push(popup);
22021     },
22022     hideAll : function()
22023     {
22024         this.popups.forEach(function(p) {
22025             p.hide();
22026         });
22027     },
22028     onShow : function() {
22029         Roo.bootstrap.Popover.popups.push(this);
22030     },
22031     onHide : function() {
22032         Roo.bootstrap.Popover.popups.remove(this);
22033     } 
22034
22035 });
22036 /**
22037  * @class Roo.bootstrap.PopoverNav
22038  * @extends Roo.bootstrap.nav.Simplebar
22039  * @parent Roo.bootstrap.Popover
22040  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22041  * @licence LGPL
22042  * Bootstrap Popover header navigation class
22043  * FIXME? should this go under nav?
22044  *
22045  * 
22046  * @constructor
22047  * Create a new Popover Header Navigation 
22048  * @param {Object} config The config object
22049  */
22050
22051 Roo.bootstrap.PopoverNav = function(config){
22052     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22053 };
22054
22055 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22056     
22057     
22058     container_method : 'getPopoverHeader' 
22059     
22060      
22061     
22062     
22063    
22064 });
22065
22066  
22067
22068  /*
22069  * - LGPL
22070  *
22071  * Progress
22072  * 
22073  */
22074
22075 /**
22076  * @class Roo.bootstrap.Progress
22077  * @extends Roo.bootstrap.Component
22078  * @children Roo.bootstrap.ProgressBar
22079  * Bootstrap Progress class
22080  * @cfg {Boolean} striped striped of the progress bar
22081  * @cfg {Boolean} active animated of the progress bar
22082  * 
22083  * 
22084  * @constructor
22085  * Create a new Progress
22086  * @param {Object} config The config object
22087  */
22088
22089 Roo.bootstrap.Progress = function(config){
22090     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22091 };
22092
22093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22094     
22095     striped : false,
22096     active: false,
22097     
22098     getAutoCreate : function(){
22099         var cfg = {
22100             tag: 'div',
22101             cls: 'progress'
22102         };
22103         
22104         
22105         if(this.striped){
22106             cfg.cls += ' progress-striped';
22107         }
22108       
22109         if(this.active){
22110             cfg.cls += ' active';
22111         }
22112         
22113         
22114         return cfg;
22115     }
22116    
22117 });
22118
22119  
22120
22121  /*
22122  * - LGPL
22123  *
22124  * ProgressBar
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.ProgressBar
22130  * @extends Roo.bootstrap.Component
22131  * Bootstrap ProgressBar class
22132  * @cfg {Number} aria_valuenow aria-value now
22133  * @cfg {Number} aria_valuemin aria-value min
22134  * @cfg {Number} aria_valuemax aria-value max
22135  * @cfg {String} label label for the progress bar
22136  * @cfg {String} panel (success | info | warning | danger )
22137  * @cfg {String} role role of the progress bar
22138  * @cfg {String} sr_only text
22139  * 
22140  * 
22141  * @constructor
22142  * Create a new ProgressBar
22143  * @param {Object} config The config object
22144  */
22145
22146 Roo.bootstrap.ProgressBar = function(config){
22147     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22148 };
22149
22150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22151     
22152     aria_valuenow : 0,
22153     aria_valuemin : 0,
22154     aria_valuemax : 100,
22155     label : false,
22156     panel : false,
22157     role : false,
22158     sr_only: false,
22159     
22160     getAutoCreate : function()
22161     {
22162         
22163         var cfg = {
22164             tag: 'div',
22165             cls: 'progress-bar',
22166             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22167         };
22168         
22169         if(this.sr_only){
22170             cfg.cn = {
22171                 tag: 'span',
22172                 cls: 'sr-only',
22173                 html: this.sr_only
22174             }
22175         }
22176         
22177         if(this.role){
22178             cfg.role = this.role;
22179         }
22180         
22181         if(this.aria_valuenow){
22182             cfg['aria-valuenow'] = this.aria_valuenow;
22183         }
22184         
22185         if(this.aria_valuemin){
22186             cfg['aria-valuemin'] = this.aria_valuemin;
22187         }
22188         
22189         if(this.aria_valuemax){
22190             cfg['aria-valuemax'] = this.aria_valuemax;
22191         }
22192         
22193         if(this.label && !this.sr_only){
22194             cfg.html = this.label;
22195         }
22196         
22197         if(this.panel){
22198             cfg.cls += ' progress-bar-' + this.panel;
22199         }
22200         
22201         return cfg;
22202     },
22203     
22204     update : function(aria_valuenow)
22205     {
22206         this.aria_valuenow = aria_valuenow;
22207         
22208         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22209     }
22210    
22211 });
22212
22213  
22214
22215  /**
22216  * @class Roo.bootstrap.TabGroup
22217  * @extends Roo.bootstrap.Column
22218  * @children Roo.bootstrap.TabPanel
22219  * Bootstrap Column class
22220  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22221  * @cfg {Boolean} carousel true to make the group behave like a carousel
22222  * @cfg {Boolean} bullets show bullets for the panels
22223  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22224  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22225  * @cfg {Boolean} showarrow (true|false) show arrow default true
22226  * 
22227  * @constructor
22228  * Create a new TabGroup
22229  * @param {Object} config The config object
22230  */
22231
22232 Roo.bootstrap.TabGroup = function(config){
22233     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22234     if (!this.navId) {
22235         this.navId = Roo.id();
22236     }
22237     this.tabs = [];
22238     Roo.bootstrap.TabGroup.register(this);
22239     
22240 };
22241
22242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22243     
22244     carousel : false,
22245     transition : false,
22246     bullets : 0,
22247     timer : 0,
22248     autoslide : false,
22249     slideFn : false,
22250     slideOnTouch : false,
22251     showarrow : true,
22252     
22253     getAutoCreate : function()
22254     {
22255         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22256         
22257         cfg.cls += ' tab-content';
22258         
22259         if (this.carousel) {
22260             cfg.cls += ' carousel slide';
22261             
22262             cfg.cn = [{
22263                cls : 'carousel-inner',
22264                cn : []
22265             }];
22266         
22267             if(this.bullets  && !Roo.isTouch){
22268                 
22269                 var bullets = {
22270                     cls : 'carousel-bullets',
22271                     cn : []
22272                 };
22273                
22274                 if(this.bullets_cls){
22275                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22276                 }
22277                 
22278                 bullets.cn.push({
22279                     cls : 'clear'
22280                 });
22281                 
22282                 cfg.cn[0].cn.push(bullets);
22283             }
22284             
22285             if(this.showarrow){
22286                 cfg.cn[0].cn.push({
22287                     tag : 'div',
22288                     class : 'carousel-arrow',
22289                     cn : [
22290                         {
22291                             tag : 'div',
22292                             class : 'carousel-prev',
22293                             cn : [
22294                                 {
22295                                     tag : 'i',
22296                                     class : 'fa fa-chevron-left'
22297                                 }
22298                             ]
22299                         },
22300                         {
22301                             tag : 'div',
22302                             class : 'carousel-next',
22303                             cn : [
22304                                 {
22305                                     tag : 'i',
22306                                     class : 'fa fa-chevron-right'
22307                                 }
22308                             ]
22309                         }
22310                     ]
22311                 });
22312             }
22313             
22314         }
22315         
22316         return cfg;
22317     },
22318     
22319     initEvents:  function()
22320     {
22321 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22322 //            this.el.on("touchstart", this.onTouchStart, this);
22323 //        }
22324         
22325         if(this.autoslide){
22326             var _this = this;
22327             
22328             this.slideFn = window.setInterval(function() {
22329                 _this.showPanelNext();
22330             }, this.timer);
22331         }
22332         
22333         if(this.showarrow){
22334             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22335             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22336         }
22337         
22338         
22339     },
22340     
22341 //    onTouchStart : function(e, el, o)
22342 //    {
22343 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22344 //            return;
22345 //        }
22346 //        
22347 //        this.showPanelNext();
22348 //    },
22349     
22350     
22351     getChildContainer : function()
22352     {
22353         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22354     },
22355     
22356     /**
22357     * register a Navigation item
22358     * @param {Roo.bootstrap.nav.Item} the navitem to add
22359     */
22360     register : function(item)
22361     {
22362         this.tabs.push( item);
22363         item.navId = this.navId; // not really needed..
22364         this.addBullet();
22365     
22366     },
22367     
22368     getActivePanel : function()
22369     {
22370         var r = false;
22371         Roo.each(this.tabs, function(t) {
22372             if (t.active) {
22373                 r = t;
22374                 return false;
22375             }
22376             return null;
22377         });
22378         return r;
22379         
22380     },
22381     getPanelByName : function(n)
22382     {
22383         var r = false;
22384         Roo.each(this.tabs, function(t) {
22385             if (t.tabId == n) {
22386                 r = t;
22387                 return false;
22388             }
22389             return null;
22390         });
22391         return r;
22392     },
22393     indexOfPanel : function(p)
22394     {
22395         var r = false;
22396         Roo.each(this.tabs, function(t,i) {
22397             if (t.tabId == p.tabId) {
22398                 r = i;
22399                 return false;
22400             }
22401             return null;
22402         });
22403         return r;
22404     },
22405     /**
22406      * show a specific panel
22407      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22408      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22409      */
22410     showPanel : function (pan)
22411     {
22412         if(this.transition || typeof(pan) == 'undefined'){
22413             Roo.log("waiting for the transitionend");
22414             return false;
22415         }
22416         
22417         if (typeof(pan) == 'number') {
22418             pan = this.tabs[pan];
22419         }
22420         
22421         if (typeof(pan) == 'string') {
22422             pan = this.getPanelByName(pan);
22423         }
22424         
22425         var cur = this.getActivePanel();
22426         
22427         if(!pan || !cur){
22428             Roo.log('pan or acitve pan is undefined');
22429             return false;
22430         }
22431         
22432         if (pan.tabId == this.getActivePanel().tabId) {
22433             return true;
22434         }
22435         
22436         if (false === cur.fireEvent('beforedeactivate')) {
22437             return false;
22438         }
22439         
22440         if(this.bullets > 0 && !Roo.isTouch){
22441             this.setActiveBullet(this.indexOfPanel(pan));
22442         }
22443         
22444         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22445             
22446             //class="carousel-item carousel-item-next carousel-item-left"
22447             
22448             this.transition = true;
22449             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22450             var lr = dir == 'next' ? 'left' : 'right';
22451             pan.el.addClass(dir); // or prev
22452             pan.el.addClass('carousel-item-' + dir); // or prev
22453             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22454             cur.el.addClass(lr); // or right
22455             pan.el.addClass(lr);
22456             cur.el.addClass('carousel-item-' +lr); // or right
22457             pan.el.addClass('carousel-item-' +lr);
22458             
22459             
22460             var _this = this;
22461             cur.el.on('transitionend', function() {
22462                 Roo.log("trans end?");
22463                 
22464                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22465                 pan.setActive(true);
22466                 
22467                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22468                 cur.setActive(false);
22469                 
22470                 _this.transition = false;
22471                 
22472             }, this, { single:  true } );
22473             
22474             return true;
22475         }
22476         
22477         cur.setActive(false);
22478         pan.setActive(true);
22479         
22480         return true;
22481         
22482     },
22483     showPanelNext : function()
22484     {
22485         var i = this.indexOfPanel(this.getActivePanel());
22486         
22487         if (i >= this.tabs.length - 1 && !this.autoslide) {
22488             return;
22489         }
22490         
22491         if (i >= this.tabs.length - 1 && this.autoslide) {
22492             i = -1;
22493         }
22494         
22495         this.showPanel(this.tabs[i+1]);
22496     },
22497     
22498     showPanelPrev : function()
22499     {
22500         var i = this.indexOfPanel(this.getActivePanel());
22501         
22502         if (i  < 1 && !this.autoslide) {
22503             return;
22504         }
22505         
22506         if (i < 1 && this.autoslide) {
22507             i = this.tabs.length;
22508         }
22509         
22510         this.showPanel(this.tabs[i-1]);
22511     },
22512     
22513     
22514     addBullet: function()
22515     {
22516         if(!this.bullets || Roo.isTouch){
22517             return;
22518         }
22519         var ctr = this.el.select('.carousel-bullets',true).first();
22520         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22521         var bullet = ctr.createChild({
22522             cls : 'bullet bullet-' + i
22523         },ctr.dom.lastChild);
22524         
22525         
22526         var _this = this;
22527         
22528         bullet.on('click', (function(e, el, o, ii, t){
22529
22530             e.preventDefault();
22531
22532             this.showPanel(ii);
22533
22534             if(this.autoslide && this.slideFn){
22535                 clearInterval(this.slideFn);
22536                 this.slideFn = window.setInterval(function() {
22537                     _this.showPanelNext();
22538                 }, this.timer);
22539             }
22540
22541         }).createDelegate(this, [i, bullet], true));
22542                 
22543         
22544     },
22545      
22546     setActiveBullet : function(i)
22547     {
22548         if(Roo.isTouch){
22549             return;
22550         }
22551         
22552         Roo.each(this.el.select('.bullet', true).elements, function(el){
22553             el.removeClass('selected');
22554         });
22555
22556         var bullet = this.el.select('.bullet-' + i, true).first();
22557         
22558         if(!bullet){
22559             return;
22560         }
22561         
22562         bullet.addClass('selected');
22563     }
22564     
22565     
22566   
22567 });
22568
22569  
22570
22571  
22572  
22573 Roo.apply(Roo.bootstrap.TabGroup, {
22574     
22575     groups: {},
22576      /**
22577     * register a Navigation Group
22578     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22579     */
22580     register : function(navgrp)
22581     {
22582         this.groups[navgrp.navId] = navgrp;
22583         
22584     },
22585     /**
22586     * fetch a Navigation Group based on the navigation ID
22587     * if one does not exist , it will get created.
22588     * @param {string} the navgroup to add
22589     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22590     */
22591     get: function(navId) {
22592         if (typeof(this.groups[navId]) == 'undefined') {
22593             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22594         }
22595         return this.groups[navId] ;
22596     }
22597     
22598     
22599     
22600 });
22601
22602  /*
22603  * - LGPL
22604  *
22605  * TabPanel
22606  * 
22607  */
22608
22609 /**
22610  * @class Roo.bootstrap.TabPanel
22611  * @extends Roo.bootstrap.Component
22612  * @children Roo.bootstrap.Component
22613  * Bootstrap TabPanel class
22614  * @cfg {Boolean} active panel active
22615  * @cfg {String} html panel content
22616  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22617  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22618  * @cfg {String} href click to link..
22619  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22620  * 
22621  * 
22622  * @constructor
22623  * Create a new TabPanel
22624  * @param {Object} config The config object
22625  */
22626
22627 Roo.bootstrap.TabPanel = function(config){
22628     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22629     this.addEvents({
22630         /**
22631              * @event changed
22632              * Fires when the active status changes
22633              * @param {Roo.bootstrap.TabPanel} this
22634              * @param {Boolean} state the new state
22635             
22636          */
22637         'changed': true,
22638         /**
22639              * @event beforedeactivate
22640              * Fires before a tab is de-activated - can be used to do validation on a form.
22641              * @param {Roo.bootstrap.TabPanel} this
22642              * @return {Boolean} false if there is an error
22643             
22644          */
22645         'beforedeactivate': true
22646      });
22647     
22648     this.tabId = this.tabId || Roo.id();
22649   
22650 };
22651
22652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22653     
22654     active: false,
22655     html: false,
22656     tabId: false,
22657     navId : false,
22658     href : '',
22659     touchSlide : false,
22660     getAutoCreate : function(){
22661         
22662         
22663         var cfg = {
22664             tag: 'div',
22665             // item is needed for carousel - not sure if it has any effect otherwise
22666             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22667             html: this.html || ''
22668         };
22669         
22670         if(this.active){
22671             cfg.cls += ' active';
22672         }
22673         
22674         if(this.tabId){
22675             cfg.tabId = this.tabId;
22676         }
22677         
22678         
22679         
22680         return cfg;
22681     },
22682     
22683     initEvents:  function()
22684     {
22685         var p = this.parent();
22686         
22687         this.navId = this.navId || p.navId;
22688         
22689         if (typeof(this.navId) != 'undefined') {
22690             // not really needed.. but just in case.. parent should be a NavGroup.
22691             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22692             
22693             tg.register(this);
22694             
22695             var i = tg.tabs.length - 1;
22696             
22697             if(this.active && tg.bullets > 0 && i < tg.bullets){
22698                 tg.setActiveBullet(i);
22699             }
22700         }
22701         
22702         this.el.on('click', this.onClick, this);
22703         
22704         if(Roo.isTouch && this.touchSlide){
22705             this.el.on("touchstart", this.onTouchStart, this);
22706             this.el.on("touchmove", this.onTouchMove, this);
22707             this.el.on("touchend", this.onTouchEnd, this);
22708         }
22709         
22710     },
22711     
22712     onRender : function(ct, position)
22713     {
22714         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22715     },
22716     
22717     setActive : function(state)
22718     {
22719         Roo.log("panel - set active " + this.tabId + "=" + state);
22720         
22721         this.active = state;
22722         if (!state) {
22723             this.el.removeClass('active');
22724             
22725         } else  if (!this.el.hasClass('active')) {
22726             this.el.addClass('active');
22727         }
22728         
22729         this.fireEvent('changed', this, state);
22730     },
22731     
22732     onClick : function(e)
22733     {
22734         e.preventDefault();
22735         
22736         if(!this.href.length){
22737             return;
22738         }
22739         
22740         window.location.href = this.href;
22741     },
22742     
22743     startX : 0,
22744     startY : 0,
22745     endX : 0,
22746     endY : 0,
22747     swiping : false,
22748     
22749     onTouchStart : function(e)
22750     {
22751         this.swiping = false;
22752         
22753         this.startX = e.browserEvent.touches[0].clientX;
22754         this.startY = e.browserEvent.touches[0].clientY;
22755     },
22756     
22757     onTouchMove : function(e)
22758     {
22759         this.swiping = true;
22760         
22761         this.endX = e.browserEvent.touches[0].clientX;
22762         this.endY = e.browserEvent.touches[0].clientY;
22763     },
22764     
22765     onTouchEnd : function(e)
22766     {
22767         if(!this.swiping){
22768             this.onClick(e);
22769             return;
22770         }
22771         
22772         var tabGroup = this.parent();
22773         
22774         if(this.endX > this.startX){ // swiping right
22775             tabGroup.showPanelPrev();
22776             return;
22777         }
22778         
22779         if(this.startX > this.endX){ // swiping left
22780             tabGroup.showPanelNext();
22781             return;
22782         }
22783     }
22784     
22785     
22786 });
22787  
22788
22789  
22790
22791  /*
22792  * - LGPL
22793  *
22794  * DateField
22795  * 
22796  */
22797
22798 /**
22799  * @class Roo.bootstrap.form.DateField
22800  * @extends Roo.bootstrap.form.Input
22801  * Bootstrap DateField class
22802  * @cfg {Number} weekStart default 0
22803  * @cfg {String} viewMode default empty, (months|years)
22804  * @cfg {String} minViewMode default empty, (months|years)
22805  * @cfg {Number} startDate default -Infinity
22806  * @cfg {Number} endDate default Infinity
22807  * @cfg {Boolean} todayHighlight default false
22808  * @cfg {Boolean} todayBtn default false
22809  * @cfg {Boolean} calendarWeeks default false
22810  * @cfg {Object} daysOfWeekDisabled default empty
22811  * @cfg {Boolean} singleMode default false (true | false)
22812  * 
22813  * @cfg {Boolean} keyboardNavigation default true
22814  * @cfg {String} language default en
22815  * 
22816  * @constructor
22817  * Create a new DateField
22818  * @param {Object} config The config object
22819  */
22820
22821 Roo.bootstrap.form.DateField = function(config){
22822     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22823      this.addEvents({
22824             /**
22825              * @event show
22826              * Fires when this field show.
22827              * @param {Roo.bootstrap.form.DateField} this
22828              * @param {Mixed} date The date value
22829              */
22830             show : true,
22831             /**
22832              * @event show
22833              * Fires when this field hide.
22834              * @param {Roo.bootstrap.form.DateField} this
22835              * @param {Mixed} date The date value
22836              */
22837             hide : true,
22838             /**
22839              * @event select
22840              * Fires when select a date.
22841              * @param {Roo.bootstrap.form.DateField} this
22842              * @param {Mixed} date The date value
22843              */
22844             select : true,
22845             /**
22846              * @event beforeselect
22847              * Fires when before select a date.
22848              * @param {Roo.bootstrap.form.DateField} this
22849              * @param {Mixed} date The date value
22850              */
22851             beforeselect : true
22852         });
22853 };
22854
22855 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22856     
22857     /**
22858      * @cfg {String} format
22859      * The default date format string which can be overriden for localization support.  The format must be
22860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22861      */
22862     format : "m/d/y",
22863     /**
22864      * @cfg {String} altFormats
22865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22867      */
22868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22869     
22870     weekStart : 0,
22871     
22872     viewMode : '',
22873     
22874     minViewMode : '',
22875     
22876     todayHighlight : false,
22877     
22878     todayBtn: false,
22879     
22880     language: 'en',
22881     
22882     keyboardNavigation: true,
22883     
22884     calendarWeeks: false,
22885     
22886     startDate: -Infinity,
22887     
22888     endDate: Infinity,
22889     
22890     daysOfWeekDisabled: [],
22891     
22892     _events: [],
22893     
22894     singleMode : false,
22895     
22896     UTCDate: function()
22897     {
22898         return new Date(Date.UTC.apply(Date, arguments));
22899     },
22900     
22901     UTCToday: function()
22902     {
22903         var today = new Date();
22904         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22905     },
22906     
22907     getDate: function() {
22908             var d = this.getUTCDate();
22909             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22910     },
22911     
22912     getUTCDate: function() {
22913             return this.date;
22914     },
22915     
22916     setDate: function(d) {
22917             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22918     },
22919     
22920     setUTCDate: function(d) {
22921             this.date = d;
22922             this.setValue(this.formatDate(this.date));
22923     },
22924         
22925     onRender: function(ct, position)
22926     {
22927         
22928         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22929         
22930         this.language = this.language || 'en';
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22932         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22933         
22934         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22935         this.format = this.format || 'm/d/y';
22936         this.isInline = false;
22937         this.isInput = true;
22938         this.component = this.el.select('.add-on', true).first() || false;
22939         this.component = (this.component && this.component.length === 0) ? false : this.component;
22940         this.hasInput = this.component && this.inputEl().length;
22941         
22942         if (typeof(this.minViewMode === 'string')) {
22943             switch (this.minViewMode) {
22944                 case 'months':
22945                     this.minViewMode = 1;
22946                     break;
22947                 case 'years':
22948                     this.minViewMode = 2;
22949                     break;
22950                 default:
22951                     this.minViewMode = 0;
22952                     break;
22953             }
22954         }
22955         
22956         if (typeof(this.viewMode === 'string')) {
22957             switch (this.viewMode) {
22958                 case 'months':
22959                     this.viewMode = 1;
22960                     break;
22961                 case 'years':
22962                     this.viewMode = 2;
22963                     break;
22964                 default:
22965                     this.viewMode = 0;
22966                     break;
22967             }
22968         }
22969                 
22970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22971         
22972 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22973         
22974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22975         
22976         this.picker().on('mousedown', this.onMousedown, this);
22977         this.picker().on('click', this.onClick, this);
22978         
22979         this.picker().addClass('datepicker-dropdown');
22980         
22981         this.startViewMode = this.viewMode;
22982         
22983         if(this.singleMode){
22984             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22985                 v.setVisibilityMode(Roo.Element.DISPLAY);
22986                 v.hide();
22987             });
22988             
22989             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990                 v.setStyle('width', '189px');
22991             });
22992         }
22993         
22994         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22995             if(!this.calendarWeeks){
22996                 v.remove();
22997                 return;
22998             }
22999             
23000             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23001             v.attr('colspan', function(i, val){
23002                 return parseInt(val) + 1;
23003             });
23004         });
23005                         
23006         
23007         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23008         
23009         this.setStartDate(this.startDate);
23010         this.setEndDate(this.endDate);
23011         
23012         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23013         
23014         this.fillDow();
23015         this.fillMonths();
23016         this.update();
23017         this.showMode();
23018         
23019         if(this.isInline) {
23020             this.showPopup();
23021         }
23022     },
23023     
23024     picker : function()
23025     {
23026         return this.pickerEl;
23027 //        return this.el.select('.datepicker', true).first();
23028     },
23029     
23030     fillDow: function()
23031     {
23032         var dowCnt = this.weekStart;
23033         
23034         var dow = {
23035             tag: 'tr',
23036             cn: [
23037                 
23038             ]
23039         };
23040         
23041         if(this.calendarWeeks){
23042             dow.cn.push({
23043                 tag: 'th',
23044                 cls: 'cw',
23045                 html: '&nbsp;'
23046             })
23047         }
23048         
23049         while (dowCnt < this.weekStart + 7) {
23050             dow.cn.push({
23051                 tag: 'th',
23052                 cls: 'dow',
23053                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23054             });
23055         }
23056         
23057         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23058     },
23059     
23060     fillMonths: function()
23061     {    
23062         var i = 0;
23063         var months = this.picker().select('>.datepicker-months td', true).first();
23064         
23065         months.dom.innerHTML = '';
23066         
23067         while (i < 12) {
23068             var month = {
23069                 tag: 'span',
23070                 cls: 'month',
23071                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23072             };
23073             
23074             months.createChild(month);
23075         }
23076         
23077     },
23078     
23079     update: function()
23080     {
23081         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
23082         
23083         if (this.date < this.startDate) {
23084             this.viewDate = new Date(this.startDate);
23085         } else if (this.date > this.endDate) {
23086             this.viewDate = new Date(this.endDate);
23087         } else {
23088             this.viewDate = new Date(this.date);
23089         }
23090         
23091         this.fill();
23092     },
23093     
23094     fill: function() 
23095     {
23096         var d = new Date(this.viewDate),
23097                 year = d.getUTCFullYear(),
23098                 month = d.getUTCMonth(),
23099                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23100                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23101                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23102                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23103                 currentDate = this.date && this.date.valueOf(),
23104                 today = this.UTCToday();
23105         
23106         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23107         
23108 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23109         
23110 //        this.picker.select('>tfoot th.today').
23111 //                                              .text(dates[this.language].today)
23112 //                                              .toggle(this.todayBtn !== false);
23113     
23114         this.updateNavArrows();
23115         this.fillMonths();
23116                                                 
23117         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23118         
23119         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23120          
23121         prevMonth.setUTCDate(day);
23122         
23123         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23124         
23125         var nextMonth = new Date(prevMonth);
23126         
23127         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23128         
23129         nextMonth = nextMonth.valueOf();
23130         
23131         var fillMonths = false;
23132         
23133         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23134         
23135         while(prevMonth.valueOf() <= nextMonth) {
23136             var clsName = '';
23137             
23138             if (prevMonth.getUTCDay() === this.weekStart) {
23139                 if(fillMonths){
23140                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23141                 }
23142                     
23143                 fillMonths = {
23144                     tag: 'tr',
23145                     cn: []
23146                 };
23147                 
23148                 if(this.calendarWeeks){
23149                     // ISO 8601: First week contains first thursday.
23150                     // ISO also states week starts on Monday, but we can be more abstract here.
23151                     var
23152                     // Start of current week: based on weekstart/current date
23153                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23154                     // Thursday of this week
23155                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23156                     // First Thursday of year, year from thursday
23157                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23158                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23159                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23160                     
23161                     fillMonths.cn.push({
23162                         tag: 'td',
23163                         cls: 'cw',
23164                         html: calWeek
23165                     });
23166                 }
23167             }
23168             
23169             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23170                 clsName += ' old';
23171             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23172                 clsName += ' new';
23173             }
23174             if (this.todayHighlight &&
23175                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23176                 prevMonth.getUTCMonth() == today.getMonth() &&
23177                 prevMonth.getUTCDate() == today.getDate()) {
23178                 clsName += ' today';
23179             }
23180             
23181             if (currentDate && prevMonth.valueOf() === currentDate) {
23182                 clsName += ' active';
23183             }
23184             
23185             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23186                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23187                     clsName += ' disabled';
23188             }
23189             
23190             fillMonths.cn.push({
23191                 tag: 'td',
23192                 cls: 'day ' + clsName,
23193                 html: prevMonth.getDate()
23194             });
23195             
23196             prevMonth.setDate(prevMonth.getDate()+1);
23197         }
23198           
23199         var currentYear = this.date && this.date.getUTCFullYear();
23200         var currentMonth = this.date && this.date.getUTCMonth();
23201         
23202         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23203         
23204         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23205             v.removeClass('active');
23206             
23207             if(currentYear === year && k === currentMonth){
23208                 v.addClass('active');
23209             }
23210             
23211             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23212                 v.addClass('disabled');
23213             }
23214             
23215         });
23216         
23217         
23218         year = parseInt(year/10, 10) * 10;
23219         
23220         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23221         
23222         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23223         
23224         year -= 1;
23225         for (var i = -1; i < 11; i++) {
23226             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23227                 tag: 'span',
23228                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23229                 html: year
23230             });
23231             
23232             year += 1;
23233         }
23234     },
23235     
23236     showMode: function(dir) 
23237     {
23238         if (dir) {
23239             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23240         }
23241         
23242         Roo.each(this.picker().select('>div',true).elements, function(v){
23243             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23244             v.hide();
23245         });
23246         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23247     },
23248     
23249     place: function()
23250     {
23251         if(this.isInline) {
23252             return;
23253         }
23254         
23255         this.picker().removeClass(['bottom', 'top']);
23256         
23257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23258             /*
23259              * place to the top of element!
23260              *
23261              */
23262             
23263             this.picker().addClass('top');
23264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23265             
23266             return;
23267         }
23268         
23269         this.picker().addClass('bottom');
23270         
23271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23272     },
23273     
23274     parseDate : function(value)
23275     {
23276         if(!value || value instanceof Date){
23277             return value;
23278         }
23279         var v = Date.parseDate(value, this.format);
23280         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23281             v = Date.parseDate(value, 'Y-m-d');
23282         }
23283         if(!v && this.altFormats){
23284             if(!this.altFormatsArray){
23285                 this.altFormatsArray = this.altFormats.split("|");
23286             }
23287             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23288                 v = Date.parseDate(value, this.altFormatsArray[i]);
23289             }
23290         }
23291         return v;
23292     },
23293     
23294     formatDate : function(date, fmt)
23295     {   
23296         return (!date || !(date instanceof Date)) ?
23297         date : date.dateFormat(fmt || this.format);
23298     },
23299     
23300     onFocus : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23303         this.showPopup();
23304     },
23305     
23306     onBlur : function()
23307     {
23308         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23309         
23310         var d = this.inputEl().getValue();
23311         
23312         this.setValue(d);
23313                 
23314         this.hidePopup();
23315     },
23316     
23317     showPopup : function()
23318     {
23319         this.picker().show();
23320         this.update();
23321         this.place();
23322         
23323         this.fireEvent('showpopup', this, this.date);
23324     },
23325     
23326     hidePopup : function()
23327     {
23328         if(this.isInline) {
23329             return;
23330         }
23331         this.picker().hide();
23332         this.viewMode = this.startViewMode;
23333         this.showMode();
23334         
23335         this.fireEvent('hidepopup', this, this.date);
23336         
23337     },
23338     
23339     onMousedown: function(e)
23340     {
23341         e.stopPropagation();
23342         e.preventDefault();
23343     },
23344     
23345     keyup: function(e)
23346     {
23347         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23348         this.update();
23349     },
23350
23351     setValue: function(v)
23352     {
23353         if(this.fireEvent('beforeselect', this, v) !== false){
23354             var d = new Date(this.parseDate(v) ).clearTime();
23355         
23356             if(isNaN(d.getTime())){
23357                 this.date = this.viewDate = '';
23358                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23359                 return;
23360             }
23361
23362             v = this.formatDate(d);
23363
23364             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23365
23366             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23367
23368             this.update();
23369
23370             this.fireEvent('select', this, this.date);
23371         }
23372     },
23373     
23374     getValue: function()
23375     {
23376         return this.formatDate(this.date);
23377     },
23378     
23379     fireKey: function(e)
23380     {
23381         if (!this.picker().isVisible()){
23382             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23383                 this.showPopup();
23384             }
23385             return;
23386         }
23387         
23388         var dateChanged = false,
23389         dir, day, month,
23390         newDate, newViewDate;
23391         
23392         switch(e.keyCode){
23393             case 27: // escape
23394                 this.hidePopup();
23395                 e.preventDefault();
23396                 break;
23397             case 37: // left
23398             case 39: // right
23399                 if (!this.keyboardNavigation) {
23400                     break;
23401                 }
23402                 dir = e.keyCode == 37 ? -1 : 1;
23403                 
23404                 if (e.ctrlKey){
23405                     newDate = this.moveYear(this.date, dir);
23406                     newViewDate = this.moveYear(this.viewDate, dir);
23407                 } else if (e.shiftKey){
23408                     newDate = this.moveMonth(this.date, dir);
23409                     newViewDate = this.moveMonth(this.viewDate, dir);
23410                 } else {
23411                     newDate = new Date(this.date);
23412                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23413                     newViewDate = new Date(this.viewDate);
23414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23415                 }
23416                 if (this.dateWithinRange(newDate)){
23417                     this.date = newDate;
23418                     this.viewDate = newViewDate;
23419                     this.setValue(this.formatDate(this.date));
23420 //                    this.update();
23421                     e.preventDefault();
23422                     dateChanged = true;
23423                 }
23424                 break;
23425             case 38: // up
23426             case 40: // down
23427                 if (!this.keyboardNavigation) {
23428                     break;
23429                 }
23430                 dir = e.keyCode == 38 ? -1 : 1;
23431                 if (e.ctrlKey){
23432                     newDate = this.moveYear(this.date, dir);
23433                     newViewDate = this.moveYear(this.viewDate, dir);
23434                 } else if (e.shiftKey){
23435                     newDate = this.moveMonth(this.date, dir);
23436                     newViewDate = this.moveMonth(this.viewDate, dir);
23437                 } else {
23438                     newDate = new Date(this.date);
23439                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23440                     newViewDate = new Date(this.viewDate);
23441                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23442                 }
23443                 if (this.dateWithinRange(newDate)){
23444                     this.date = newDate;
23445                     this.viewDate = newViewDate;
23446                     this.setValue(this.formatDate(this.date));
23447 //                    this.update();
23448                     e.preventDefault();
23449                     dateChanged = true;
23450                 }
23451                 break;
23452             case 13: // enter
23453                 this.setValue(this.formatDate(this.date));
23454                 this.hidePopup();
23455                 e.preventDefault();
23456                 break;
23457             case 9: // tab
23458                 this.setValue(this.formatDate(this.date));
23459                 this.hidePopup();
23460                 break;
23461             case 16: // shift
23462             case 17: // ctrl
23463             case 18: // alt
23464                 break;
23465             default :
23466                 this.hidePopup();
23467                 
23468         }
23469     },
23470     
23471     
23472     onClick: function(e) 
23473     {
23474         e.stopPropagation();
23475         e.preventDefault();
23476         
23477         var target = e.getTarget();
23478         
23479         if(target.nodeName.toLowerCase() === 'i'){
23480             target = Roo.get(target).dom.parentNode;
23481         }
23482         
23483         var nodeName = target.nodeName;
23484         var className = target.className;
23485         var html = target.innerHTML;
23486         //Roo.log(nodeName);
23487         
23488         switch(nodeName.toLowerCase()) {
23489             case 'th':
23490                 switch(className) {
23491                     case 'switch':
23492                         this.showMode(1);
23493                         break;
23494                     case 'prev':
23495                     case 'next':
23496                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23497                         switch(this.viewMode){
23498                                 case 0:
23499                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23500                                         break;
23501                                 case 1:
23502                                 case 2:
23503                                         this.viewDate = this.moveYear(this.viewDate, dir);
23504                                         break;
23505                         }
23506                         this.fill();
23507                         break;
23508                     case 'today':
23509                         var date = new Date();
23510                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23511 //                        this.fill()
23512                         this.setValue(this.formatDate(this.date));
23513                         
23514                         this.hidePopup();
23515                         break;
23516                 }
23517                 break;
23518             case 'span':
23519                 if (className.indexOf('disabled') < 0) {
23520                 if (!this.viewDate) {
23521                     this.viewDate = new Date();
23522                 }
23523                 this.viewDate.setUTCDate(1);
23524                     if (className.indexOf('month') > -1) {
23525                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23526                     } else {
23527                         var year = parseInt(html, 10) || 0;
23528                         this.viewDate.setUTCFullYear(year);
23529                         
23530                     }
23531                     
23532                     if(this.singleMode){
23533                         this.setValue(this.formatDate(this.viewDate));
23534                         this.hidePopup();
23535                         return;
23536                     }
23537                     
23538                     this.showMode(-1);
23539                     this.fill();
23540                 }
23541                 break;
23542                 
23543             case 'td':
23544                 //Roo.log(className);
23545                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23546                     var day = parseInt(html, 10) || 1;
23547                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23548                         month = (this.viewDate || new Date()).getUTCMonth();
23549
23550                     if (className.indexOf('old') > -1) {
23551                         if(month === 0 ){
23552                             month = 11;
23553                             year -= 1;
23554                         }else{
23555                             month -= 1;
23556                         }
23557                     } else if (className.indexOf('new') > -1) {
23558                         if (month == 11) {
23559                             month = 0;
23560                             year += 1;
23561                         } else {
23562                             month += 1;
23563                         }
23564                     }
23565                     //Roo.log([year,month,day]);
23566                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23567                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23568 //                    this.fill();
23569                     //Roo.log(this.formatDate(this.date));
23570                     this.setValue(this.formatDate(this.date));
23571                     this.hidePopup();
23572                 }
23573                 break;
23574         }
23575     },
23576     
23577     setStartDate: function(startDate)
23578     {
23579         this.startDate = startDate || -Infinity;
23580         if (this.startDate !== -Infinity) {
23581             this.startDate = this.parseDate(this.startDate);
23582         }
23583         this.update();
23584         this.updateNavArrows();
23585     },
23586
23587     setEndDate: function(endDate)
23588     {
23589         this.endDate = endDate || Infinity;
23590         if (this.endDate !== Infinity) {
23591             this.endDate = this.parseDate(this.endDate);
23592         }
23593         this.update();
23594         this.updateNavArrows();
23595     },
23596     
23597     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23598     {
23599         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23600         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23601             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23602         }
23603         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23604             return parseInt(d, 10);
23605         });
23606         this.update();
23607         this.updateNavArrows();
23608     },
23609     
23610     updateNavArrows: function() 
23611     {
23612         if(this.singleMode){
23613             return;
23614         }
23615         
23616         var d = new Date(this.viewDate),
23617         year = d.getUTCFullYear(),
23618         month = d.getUTCMonth();
23619         
23620         Roo.each(this.picker().select('.prev', true).elements, function(v){
23621             v.show();
23622             switch (this.viewMode) {
23623                 case 0:
23624
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23626                         v.hide();
23627                     }
23628                     break;
23629                 case 1:
23630                 case 2:
23631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23632                         v.hide();
23633                     }
23634                     break;
23635             }
23636         });
23637         
23638         Roo.each(this.picker().select('.next', true).elements, function(v){
23639             v.show();
23640             switch (this.viewMode) {
23641                 case 0:
23642
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23644                         v.hide();
23645                     }
23646                     break;
23647                 case 1:
23648                 case 2:
23649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23650                         v.hide();
23651                     }
23652                     break;
23653             }
23654         })
23655     },
23656     
23657     moveMonth: function(date, dir)
23658     {
23659         if (!dir) {
23660             return date;
23661         }
23662         var new_date = new Date(date.valueOf()),
23663         day = new_date.getUTCDate(),
23664         month = new_date.getUTCMonth(),
23665         mag = Math.abs(dir),
23666         new_month, test;
23667         dir = dir > 0 ? 1 : -1;
23668         if (mag == 1){
23669             test = dir == -1
23670             // If going back one month, make sure month is not current month
23671             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23672             ? function(){
23673                 return new_date.getUTCMonth() == month;
23674             }
23675             // If going forward one month, make sure month is as expected
23676             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23677             : function(){
23678                 return new_date.getUTCMonth() != new_month;
23679             };
23680             new_month = month + dir;
23681             new_date.setUTCMonth(new_month);
23682             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23683             if (new_month < 0 || new_month > 11) {
23684                 new_month = (new_month + 12) % 12;
23685             }
23686         } else {
23687             // For magnitudes >1, move one month at a time...
23688             for (var i=0; i<mag; i++) {
23689                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23690                 new_date = this.moveMonth(new_date, dir);
23691             }
23692             // ...then reset the day, keeping it in the new month
23693             new_month = new_date.getUTCMonth();
23694             new_date.setUTCDate(day);
23695             test = function(){
23696                 return new_month != new_date.getUTCMonth();
23697             };
23698         }
23699         // Common date-resetting loop -- if date is beyond end of month, make it
23700         // end of month
23701         while (test()){
23702             new_date.setUTCDate(--day);
23703             new_date.setUTCMonth(new_month);
23704         }
23705         return new_date;
23706     },
23707
23708     moveYear: function(date, dir)
23709     {
23710         return this.moveMonth(date, dir*12);
23711     },
23712
23713     dateWithinRange: function(date)
23714     {
23715         return date >= this.startDate && date <= this.endDate;
23716     },
23717
23718     
23719     remove: function() 
23720     {
23721         this.picker().remove();
23722     },
23723     
23724     validateValue : function(value)
23725     {
23726         if(this.getVisibilityEl().hasClass('hidden')){
23727             return true;
23728         }
23729         
23730         if(value.length < 1)  {
23731             if(this.allowBlank){
23732                 return true;
23733             }
23734             return false;
23735         }
23736         
23737         if(value.length < this.minLength){
23738             return false;
23739         }
23740         if(value.length > this.maxLength){
23741             return false;
23742         }
23743         if(this.vtype){
23744             var vt = Roo.form.VTypes;
23745             if(!vt[this.vtype](value, this)){
23746                 return false;
23747             }
23748         }
23749         if(typeof this.validator == "function"){
23750             var msg = this.validator(value);
23751             if(msg !== true){
23752                 return false;
23753             }
23754         }
23755         
23756         if(this.regex && !this.regex.test(value)){
23757             return false;
23758         }
23759         
23760         if(typeof(this.parseDate(value)) == 'undefined'){
23761             return false;
23762         }
23763         
23764         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23765             return false;
23766         }      
23767         
23768         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23769             return false;
23770         } 
23771         
23772         
23773         return true;
23774     },
23775     
23776     reset : function()
23777     {
23778         this.date = this.viewDate = '';
23779         
23780         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23781     }
23782    
23783 });
23784
23785 Roo.apply(Roo.bootstrap.form.DateField,  {
23786     
23787     head : {
23788         tag: 'thead',
23789         cn: [
23790         {
23791             tag: 'tr',
23792             cn: [
23793             {
23794                 tag: 'th',
23795                 cls: 'prev',
23796                 html: '<i class="fa fa-arrow-left"/>'
23797             },
23798             {
23799                 tag: 'th',
23800                 cls: 'switch',
23801                 colspan: '5'
23802             },
23803             {
23804                 tag: 'th',
23805                 cls: 'next',
23806                 html: '<i class="fa fa-arrow-right"/>'
23807             }
23808
23809             ]
23810         }
23811         ]
23812     },
23813     
23814     content : {
23815         tag: 'tbody',
23816         cn: [
23817         {
23818             tag: 'tr',
23819             cn: [
23820             {
23821                 tag: 'td',
23822                 colspan: '7'
23823             }
23824             ]
23825         }
23826         ]
23827     },
23828     
23829     footer : {
23830         tag: 'tfoot',
23831         cn: [
23832         {
23833             tag: 'tr',
23834             cn: [
23835             {
23836                 tag: 'th',
23837                 colspan: '7',
23838                 cls: 'today'
23839             }
23840                     
23841             ]
23842         }
23843         ]
23844     },
23845     
23846     dates:{
23847         en: {
23848             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23849             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23850             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23853             today: "Today"
23854         }
23855     },
23856     
23857     modes: [
23858     {
23859         clsName: 'days',
23860         navFnc: 'Month',
23861         navStep: 1
23862     },
23863     {
23864         clsName: 'months',
23865         navFnc: 'FullYear',
23866         navStep: 1
23867     },
23868     {
23869         clsName: 'years',
23870         navFnc: 'FullYear',
23871         navStep: 10
23872     }]
23873 });
23874
23875 Roo.apply(Roo.bootstrap.form.DateField,  {
23876   
23877     template : {
23878         tag: 'div',
23879         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23880         cn: [
23881         {
23882             tag: 'div',
23883             cls: 'datepicker-days',
23884             cn: [
23885             {
23886                 tag: 'table',
23887                 cls: 'table-condensed',
23888                 cn:[
23889                 Roo.bootstrap.form.DateField.head,
23890                 {
23891                     tag: 'tbody'
23892                 },
23893                 Roo.bootstrap.form.DateField.footer
23894                 ]
23895             }
23896             ]
23897         },
23898         {
23899             tag: 'div',
23900             cls: 'datepicker-months',
23901             cn: [
23902             {
23903                 tag: 'table',
23904                 cls: 'table-condensed',
23905                 cn:[
23906                 Roo.bootstrap.form.DateField.head,
23907                 Roo.bootstrap.form.DateField.content,
23908                 Roo.bootstrap.form.DateField.footer
23909                 ]
23910             }
23911             ]
23912         },
23913         {
23914             tag: 'div',
23915             cls: 'datepicker-years',
23916             cn: [
23917             {
23918                 tag: 'table',
23919                 cls: 'table-condensed',
23920                 cn:[
23921                 Roo.bootstrap.form.DateField.head,
23922                 Roo.bootstrap.form.DateField.content,
23923                 Roo.bootstrap.form.DateField.footer
23924                 ]
23925             }
23926             ]
23927         }
23928         ]
23929     }
23930 });
23931
23932  
23933
23934  /*
23935  * - LGPL
23936  *
23937  * TimeField
23938  * 
23939  */
23940
23941 /**
23942  * @class Roo.bootstrap.form.TimeField
23943  * @extends Roo.bootstrap.form.Input
23944  * Bootstrap DateField class
23945  * 
23946  * 
23947  * @constructor
23948  * Create a new TimeField
23949  * @param {Object} config The config object
23950  */
23951
23952 Roo.bootstrap.form.TimeField = function(config){
23953     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23954     this.addEvents({
23955             /**
23956              * @event show
23957              * Fires when this field show.
23958              * @param {Roo.bootstrap.form.DateField} thisthis
23959              * @param {Mixed} date The date value
23960              */
23961             show : true,
23962             /**
23963              * @event show
23964              * Fires when this field hide.
23965              * @param {Roo.bootstrap.form.DateField} this
23966              * @param {Mixed} date The date value
23967              */
23968             hide : true,
23969             /**
23970              * @event select
23971              * Fires when select a date.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             select : true
23976         });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23980     
23981     /**
23982      * @cfg {String} format
23983      * The default time format string which can be overriden for localization support.  The format must be
23984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23985      */
23986     format : "H:i",
23987
23988     getAutoCreate : function()
23989     {
23990         this.after = '<i class="fa far fa-clock"></i>';
23991         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23992         
23993          
23994     },
23995     onRender: function(ct, position)
23996     {
23997         
23998         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23999                 
24000         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24001         
24002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003         
24004         this.pop = this.picker().select('>.datepicker-time',true).first();
24005         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011     
24012         this.fillTime();
24013         this.update();
24014             
24015         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24021
24022     },
24023     
24024     fireKey: function(e){
24025         if (!this.picker().isVisible()){
24026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027                 this.show();
24028             }
24029             return;
24030         }
24031
24032         e.preventDefault();
24033         
24034         switch(e.keyCode){
24035             case 27: // escape
24036                 this.hide();
24037                 break;
24038             case 37: // left
24039             case 39: // right
24040                 this.onTogglePeriod();
24041                 break;
24042             case 38: // up
24043                 this.onIncrementMinutes();
24044                 break;
24045             case 40: // down
24046                 this.onDecrementMinutes();
24047                 break;
24048             case 13: // enter
24049             case 9: // tab
24050                 this.setTime();
24051                 break;
24052         }
24053     },
24054     
24055     onClick: function(e) {
24056         e.stopPropagation();
24057         e.preventDefault();
24058     },
24059     
24060     picker : function()
24061     {
24062         return this.pickerEl;
24063     },
24064     
24065     fillTime: function()
24066     {    
24067         var time = this.pop.select('tbody', true).first();
24068         
24069         time.dom.innerHTML = '';
24070         
24071         time.createChild({
24072             tag: 'tr',
24073             cn: [
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'hours-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         } 
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'a',
24099                             href: '#',
24100                             cls: 'btn',
24101                             cn: [
24102                                 {
24103                                     tag: 'i',
24104                                     cls: 'minutes-up fa fas fa-chevron-up'
24105                                 }
24106                             ]
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator'
24113                 }
24114             ]
24115         });
24116         
24117         time.createChild({
24118             tag: 'tr',
24119             cn: [
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'span',
24125                             cls: 'timepicker-hour',
24126                             html: '00'
24127                         }  
24128                     ]
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cls: 'separator',
24133                     html: ':'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-minute',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cn: [
24152                         {
24153                             tag: 'button',
24154                             type: 'button',
24155                             cls: 'btn btn-primary period',
24156                             html: 'AM'
24157                             
24158                         }
24159                     ]
24160                 }
24161             ]
24162         });
24163         
24164         time.createChild({
24165             tag: 'tr',
24166             cn: [
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'hours-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 },
24187                 {
24188                     tag: 'td',
24189                     cn: [
24190                         {
24191                             tag: 'a',
24192                             href: '#',
24193                             cls: 'btn',
24194                             cn: [
24195                                 {
24196                                     tag: 'span',
24197                                     cls: 'minutes-down fa fas fa-chevron-down'
24198                                 }
24199                             ]
24200                         }
24201                     ]
24202                 },
24203                 {
24204                     tag: 'td',
24205                     cls: 'separator'
24206                 }
24207             ]
24208         });
24209         
24210     },
24211     
24212     update: function()
24213     {
24214         
24215         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24216         
24217         this.fill();
24218     },
24219     
24220     fill: function() 
24221     {
24222         var hours = this.time.getHours();
24223         var minutes = this.time.getMinutes();
24224         var period = 'AM';
24225         
24226         if(hours > 11){
24227             period = 'PM';
24228         }
24229         
24230         if(hours == 0){
24231             hours = 12;
24232         }
24233         
24234         
24235         if(hours > 12){
24236             hours = hours - 12;
24237         }
24238         
24239         if(hours < 10){
24240             hours = '0' + hours;
24241         }
24242         
24243         if(minutes < 10){
24244             minutes = '0' + minutes;
24245         }
24246         
24247         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24248         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24249         this.pop.select('button', true).first().dom.innerHTML = period;
24250         
24251     },
24252     
24253     place: function()
24254     {   
24255         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24256         
24257         var cls = ['bottom'];
24258         
24259         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24260             cls.pop();
24261             cls.push('top');
24262         }
24263         
24264         cls.push('right');
24265         
24266         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24267             cls.pop();
24268             cls.push('left');
24269         }
24270         //this.picker().setXY(20000,20000);
24271         this.picker().addClass(cls.join('-'));
24272         
24273         var _this = this;
24274         
24275         Roo.each(cls, function(c){
24276             if(c == 'bottom'){
24277                 (function() {
24278                  //  
24279                 }).defer(200);
24280                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24281                 //_this.picker().setTop(_this.inputEl().getHeight());
24282                 return;
24283             }
24284             if(c == 'top'){
24285                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24286                 
24287                 //_this.picker().setTop(0 - _this.picker().getHeight());
24288                 return;
24289             }
24290             /*
24291             if(c == 'left'){
24292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24293                 return;
24294             }
24295             if(c == 'right'){
24296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24297                 return;
24298             }
24299             */
24300         });
24301         
24302     },
24303   
24304     onFocus : function()
24305     {
24306         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24307         this.show();
24308     },
24309     
24310     onBlur : function()
24311     {
24312         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313         this.hide();
24314     },
24315     
24316     show : function()
24317     {
24318         this.picker().show();
24319         this.pop.show();
24320         this.update();
24321         this.place();
24322         
24323         this.fireEvent('show', this, this.date);
24324     },
24325     
24326     hide : function()
24327     {
24328         this.picker().hide();
24329         this.pop.hide();
24330         
24331         this.fireEvent('hide', this, this.date);
24332     },
24333     
24334     setTime : function()
24335     {
24336         this.hide();
24337         this.setValue(this.time.format(this.format));
24338         
24339         this.fireEvent('select', this, this.date);
24340         
24341         
24342     },
24343     
24344     onMousedown: function(e){
24345         e.stopPropagation();
24346         e.preventDefault();
24347     },
24348     
24349     onIncrementHours: function()
24350     {
24351         Roo.log('onIncrementHours');
24352         this.time = this.time.add(Date.HOUR, 1);
24353         this.update();
24354         
24355     },
24356     
24357     onDecrementHours: function()
24358     {
24359         Roo.log('onDecrementHours');
24360         this.time = this.time.add(Date.HOUR, -1);
24361         this.update();
24362     },
24363     
24364     onIncrementMinutes: function()
24365     {
24366         Roo.log('onIncrementMinutes');
24367         this.time = this.time.add(Date.MINUTE, 1);
24368         this.update();
24369     },
24370     
24371     onDecrementMinutes: function()
24372     {
24373         Roo.log('onDecrementMinutes');
24374         this.time = this.time.add(Date.MINUTE, -1);
24375         this.update();
24376     },
24377     
24378     onTogglePeriod: function()
24379     {
24380         Roo.log('onTogglePeriod');
24381         this.time = this.time.add(Date.HOUR, 12);
24382         this.update();
24383     }
24384     
24385    
24386 });
24387  
24388
24389 Roo.apply(Roo.bootstrap.form.TimeField,  {
24390   
24391     template : {
24392         tag: 'div',
24393         cls: 'datepicker dropdown-menu',
24394         cn: [
24395             {
24396                 tag: 'div',
24397                 cls: 'datepicker-time',
24398                 cn: [
24399                 {
24400                     tag: 'table',
24401                     cls: 'table-condensed',
24402                     cn:[
24403                         {
24404                             tag: 'tbody',
24405                             cn: [
24406                                 {
24407                                     tag: 'tr',
24408                                     cn: [
24409                                     {
24410                                         tag: 'td',
24411                                         colspan: '7'
24412                                     }
24413                                     ]
24414                                 }
24415                             ]
24416                         },
24417                         {
24418                             tag: 'tfoot',
24419                             cn: [
24420                                 {
24421                                     tag: 'tr',
24422                                     cn: [
24423                                     {
24424                                         tag: 'th',
24425                                         colspan: '7',
24426                                         cls: '',
24427                                         cn: [
24428                                             {
24429                                                 tag: 'button',
24430                                                 cls: 'btn btn-info ok',
24431                                                 html: 'OK'
24432                                             }
24433                                         ]
24434                                     }
24435                     
24436                                     ]
24437                                 }
24438                             ]
24439                         }
24440                     ]
24441                 }
24442                 ]
24443             }
24444         ]
24445     }
24446 });
24447
24448  
24449
24450  /*
24451  * - LGPL
24452  *
24453  * MonthField
24454  * 
24455  */
24456
24457 /**
24458  * @class Roo.bootstrap.form.MonthField
24459  * @extends Roo.bootstrap.form.Input
24460  * Bootstrap MonthField class
24461  * 
24462  * @cfg {String} language default en
24463  * 
24464  * @constructor
24465  * Create a new MonthField
24466  * @param {Object} config The config object
24467  */
24468
24469 Roo.bootstrap.form.MonthField = function(config){
24470     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24471     
24472     this.addEvents({
24473         /**
24474          * @event show
24475          * Fires when this field show.
24476          * @param {Roo.bootstrap.form.MonthField} this
24477          * @param {Mixed} date The date value
24478          */
24479         show : true,
24480         /**
24481          * @event show
24482          * Fires when this field hide.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         hide : true,
24487         /**
24488          * @event select
24489          * Fires when select a date.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {String} oldvalue The old value
24492          * @param {String} newvalue The new value
24493          */
24494         select : true
24495     });
24496 };
24497
24498 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24499     
24500     onRender: function(ct, position)
24501     {
24502         
24503         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24504         
24505         this.language = this.language || 'en';
24506         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24507         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24508         
24509         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24510         this.isInline = false;
24511         this.isInput = true;
24512         this.component = this.el.select('.add-on', true).first() || false;
24513         this.component = (this.component && this.component.length === 0) ? false : this.component;
24514         this.hasInput = this.component && this.inputEL().length;
24515         
24516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24517         
24518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24519         
24520         this.picker().on('mousedown', this.onMousedown, this);
24521         this.picker().on('click', this.onClick, this);
24522         
24523         this.picker().addClass('datepicker-dropdown');
24524         
24525         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24526             v.setStyle('width', '189px');
24527         });
24528         
24529         this.fillMonths();
24530         
24531         this.update();
24532         
24533         if(this.isInline) {
24534             this.show();
24535         }
24536         
24537     },
24538     
24539     setValue: function(v, suppressEvent)
24540     {   
24541         var o = this.getValue();
24542         
24543         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24544         
24545         this.update();
24546
24547         if(suppressEvent !== true){
24548             this.fireEvent('select', this, o, v);
24549         }
24550         
24551     },
24552     
24553     getValue: function()
24554     {
24555         return this.value;
24556     },
24557     
24558     onClick: function(e) 
24559     {
24560         e.stopPropagation();
24561         e.preventDefault();
24562         
24563         var target = e.getTarget();
24564         
24565         if(target.nodeName.toLowerCase() === 'i'){
24566             target = Roo.get(target).dom.parentNode;
24567         }
24568         
24569         var nodeName = target.nodeName;
24570         var className = target.className;
24571         var html = target.innerHTML;
24572         
24573         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24574             return;
24575         }
24576         
24577         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24578         
24579         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24580         
24581         this.hide();
24582                         
24583     },
24584     
24585     picker : function()
24586     {
24587         return this.pickerEl;
24588     },
24589     
24590     fillMonths: function()
24591     {    
24592         var i = 0;
24593         var months = this.picker().select('>.datepicker-months td', true).first();
24594         
24595         months.dom.innerHTML = '';
24596         
24597         while (i < 12) {
24598             var month = {
24599                 tag: 'span',
24600                 cls: 'month',
24601                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24602             };
24603             
24604             months.createChild(month);
24605         }
24606         
24607     },
24608     
24609     update: function()
24610     {
24611         var _this = this;
24612         
24613         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24614             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24615         }
24616         
24617         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24618             e.removeClass('active');
24619             
24620             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24621                 e.addClass('active');
24622             }
24623         })
24624     },
24625     
24626     place: function()
24627     {
24628         if(this.isInline) {
24629             return;
24630         }
24631         
24632         this.picker().removeClass(['bottom', 'top']);
24633         
24634         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24635             /*
24636              * place to the top of element!
24637              *
24638              */
24639             
24640             this.picker().addClass('top');
24641             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24642             
24643             return;
24644         }
24645         
24646         this.picker().addClass('bottom');
24647         
24648         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24649     },
24650     
24651     onFocus : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24654         this.show();
24655     },
24656     
24657     onBlur : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24660         
24661         var d = this.inputEl().getValue();
24662         
24663         this.setValue(d);
24664                 
24665         this.hide();
24666     },
24667     
24668     show : function()
24669     {
24670         this.picker().show();
24671         this.picker().select('>.datepicker-months', true).first().show();
24672         this.update();
24673         this.place();
24674         
24675         this.fireEvent('show', this, this.date);
24676     },
24677     
24678     hide : function()
24679     {
24680         if(this.isInline) {
24681             return;
24682         }
24683         this.picker().hide();
24684         this.fireEvent('hide', this, this.date);
24685         
24686     },
24687     
24688     onMousedown: function(e)
24689     {
24690         e.stopPropagation();
24691         e.preventDefault();
24692     },
24693     
24694     keyup: function(e)
24695     {
24696         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24697         this.update();
24698     },
24699
24700     fireKey: function(e)
24701     {
24702         if (!this.picker().isVisible()){
24703             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24704                 this.show();
24705             }
24706             return;
24707         }
24708         
24709         var dir;
24710         
24711         switch(e.keyCode){
24712             case 27: // escape
24713                 this.hide();
24714                 e.preventDefault();
24715                 break;
24716             case 37: // left
24717             case 39: // right
24718                 dir = e.keyCode == 37 ? -1 : 1;
24719                 
24720                 this.vIndex = this.vIndex + dir;
24721                 
24722                 if(this.vIndex < 0){
24723                     this.vIndex = 0;
24724                 }
24725                 
24726                 if(this.vIndex > 11){
24727                     this.vIndex = 11;
24728                 }
24729                 
24730                 if(isNaN(this.vIndex)){
24731                     this.vIndex = 0;
24732                 }
24733                 
24734                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735                 
24736                 break;
24737             case 38: // up
24738             case 40: // down
24739                 
24740                 dir = e.keyCode == 38 ? -1 : 1;
24741                 
24742                 this.vIndex = this.vIndex + dir * 4;
24743                 
24744                 if(this.vIndex < 0){
24745                     this.vIndex = 0;
24746                 }
24747                 
24748                 if(this.vIndex > 11){
24749                     this.vIndex = 11;
24750                 }
24751                 
24752                 if(isNaN(this.vIndex)){
24753                     this.vIndex = 0;
24754                 }
24755                 
24756                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 break;
24758                 
24759             case 13: // enter
24760                 
24761                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24762                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 }
24764                 
24765                 this.hide();
24766                 e.preventDefault();
24767                 break;
24768             case 9: // tab
24769                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24770                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24771                 }
24772                 this.hide();
24773                 break;
24774             case 16: // shift
24775             case 17: // ctrl
24776             case 18: // alt
24777                 break;
24778             default :
24779                 this.hide();
24780                 
24781         }
24782     },
24783     
24784     remove: function() 
24785     {
24786         this.picker().remove();
24787     }
24788    
24789 });
24790
24791 Roo.apply(Roo.bootstrap.form.MonthField,  {
24792     
24793     content : {
24794         tag: 'tbody',
24795         cn: [
24796         {
24797             tag: 'tr',
24798             cn: [
24799             {
24800                 tag: 'td',
24801                 colspan: '7'
24802             }
24803             ]
24804         }
24805         ]
24806     },
24807     
24808     dates:{
24809         en: {
24810             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24811             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24812         }
24813     }
24814 });
24815
24816 Roo.apply(Roo.bootstrap.form.MonthField,  {
24817   
24818     template : {
24819         tag: 'div',
24820         cls: 'datepicker dropdown-menu roo-dynamic',
24821         cn: [
24822             {
24823                 tag: 'div',
24824                 cls: 'datepicker-months',
24825                 cn: [
24826                 {
24827                     tag: 'table',
24828                     cls: 'table-condensed',
24829                     cn:[
24830                         Roo.bootstrap.form.DateField.content
24831                     ]
24832                 }
24833                 ]
24834             }
24835         ]
24836     }
24837 });
24838
24839  
24840
24841  
24842  /*
24843  * - LGPL
24844  *
24845  * CheckBox
24846  * 
24847  */
24848
24849 /**
24850  * @class Roo.bootstrap.form.CheckBox
24851  * @extends Roo.bootstrap.form.Input
24852  * Bootstrap CheckBox class
24853  * 
24854  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24855  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24856  * @cfg {String} boxLabel The text that appears beside the checkbox
24857  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24858  * @cfg {Boolean} checked initnal the element
24859  * @cfg {Boolean} inline inline the element (default false)
24860  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24861  * @cfg {String} tooltip label tooltip
24862  * 
24863  * @constructor
24864  * Create a new CheckBox
24865  * @param {Object} config The config object
24866  */
24867
24868 Roo.bootstrap.form.CheckBox = function(config){
24869     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24870    
24871     this.addEvents({
24872         /**
24873         * @event check
24874         * Fires when the element is checked or unchecked.
24875         * @param {Roo.bootstrap.form.CheckBox} this This input
24876         * @param {Boolean} checked The new checked value
24877         */
24878        check : true,
24879        /**
24880         * @event click
24881         * Fires when the element is click.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         */
24884        click : true
24885     });
24886     
24887 };
24888
24889 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24890   
24891     inputType: 'checkbox',
24892     inputValue: 1,
24893     valueOff: 0,
24894     boxLabel: false,
24895     checked: false,
24896     weight : false,
24897     inline: false,
24898     tooltip : '',
24899     
24900     // checkbox success does not make any sense really.. 
24901     invalidClass : "",
24902     validClass : "",
24903     
24904     
24905     getAutoCreate : function()
24906     {
24907         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24908         
24909         var id = Roo.id();
24910         
24911         var cfg = {};
24912         
24913         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24914         
24915         if(this.inline){
24916             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24917         }
24918         
24919         var input =  {
24920             tag: 'input',
24921             id : id,
24922             type : this.inputType,
24923             value : this.inputValue,
24924             cls : 'roo-' + this.inputType, //'form-box',
24925             placeholder : this.placeholder || ''
24926             
24927         };
24928         
24929         if(this.inputType != 'radio'){
24930             var hidden =  {
24931                 tag: 'input',
24932                 type : 'hidden',
24933                 cls : 'roo-hidden-value',
24934                 value : this.checked ? this.inputValue : this.valueOff
24935             };
24936         }
24937         
24938             
24939         if (this.weight) { // Validity check?
24940             cfg.cls += " " + this.inputType + "-" + this.weight;
24941         }
24942         
24943         if (this.disabled) {
24944             input.disabled=true;
24945         }
24946         
24947         if(this.checked){
24948             input.checked = this.checked;
24949         }
24950         
24951         if (this.name) {
24952             
24953             input.name = this.name;
24954             
24955             if(this.inputType != 'radio'){
24956                 hidden.name = this.name;
24957                 input.name = '_hidden_' + this.name;
24958             }
24959         }
24960         
24961         if (this.size) {
24962             input.cls += ' input-' + this.size;
24963         }
24964         
24965         var settings=this;
24966         
24967         ['xs','sm','md','lg'].map(function(size){
24968             if (settings[size]) {
24969                 cfg.cls += ' col-' + size + '-' + settings[size];
24970             }
24971         });
24972         
24973         var inputblock = input;
24974          
24975         if (this.before || this.after) {
24976             
24977             inputblock = {
24978                 cls : 'input-group',
24979                 cn :  [] 
24980             };
24981             
24982             if (this.before) {
24983                 inputblock.cn.push({
24984                     tag :'span',
24985                     cls : 'input-group-addon',
24986                     html : this.before
24987                 });
24988             }
24989             
24990             inputblock.cn.push(input);
24991             
24992             if(this.inputType != 'radio'){
24993                 inputblock.cn.push(hidden);
24994             }
24995             
24996             if (this.after) {
24997                 inputblock.cn.push({
24998                     tag :'span',
24999                     cls : 'input-group-addon',
25000                     html : this.after
25001                 });
25002             }
25003             
25004         }
25005         var boxLabelCfg = false;
25006         
25007         if(this.boxLabel){
25008            
25009             boxLabelCfg = {
25010                 tag: 'label',
25011                 //'for': id, // box label is handled by onclick - so no for...
25012                 cls: 'box-label',
25013                 html: this.boxLabel
25014             };
25015             if(this.tooltip){
25016                 boxLabelCfg.tooltip = this.tooltip;
25017             }
25018              
25019         }
25020         
25021         
25022         if (align ==='left' && this.fieldLabel.length) {
25023 //                Roo.log("left and has label");
25024             cfg.cn = [
25025                 {
25026                     tag: 'label',
25027                     'for' :  id,
25028                     cls : 'control-label',
25029                     html : this.fieldLabel
25030                 },
25031                 {
25032                     cls : "", 
25033                     cn: [
25034                         inputblock
25035                     ]
25036                 }
25037             ];
25038             
25039             if (boxLabelCfg) {
25040                 cfg.cn[1].cn.push(boxLabelCfg);
25041             }
25042             
25043             if(this.labelWidth > 12){
25044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25045             }
25046             
25047             if(this.labelWidth < 13 && this.labelmd == 0){
25048                 this.labelmd = this.labelWidth;
25049             }
25050             
25051             if(this.labellg > 0){
25052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25054             }
25055             
25056             if(this.labelmd > 0){
25057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25059             }
25060             
25061             if(this.labelsm > 0){
25062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25064             }
25065             
25066             if(this.labelxs > 0){
25067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25069             }
25070             
25071         } else if ( this.fieldLabel.length) {
25072 //                Roo.log(" label");
25073                 cfg.cn = [
25074                    
25075                     {
25076                         tag: this.boxLabel ? 'span' : 'label',
25077                         'for': id,
25078                         cls: 'control-label box-input-label',
25079                         //cls : 'input-group-addon',
25080                         html : this.fieldLabel
25081                     },
25082                     
25083                     inputblock
25084                     
25085                 ];
25086                 if (boxLabelCfg) {
25087                     cfg.cn.push(boxLabelCfg);
25088                 }
25089
25090         } else {
25091             
25092 //                Roo.log(" no label && no align");
25093                 cfg.cn = [  inputblock ] ;
25094                 if (boxLabelCfg) {
25095                     cfg.cn.push(boxLabelCfg);
25096                 }
25097
25098                 
25099         }
25100         
25101        
25102         
25103         if(this.inputType != 'radio'){
25104             cfg.cn.push(hidden);
25105         }
25106         
25107         return cfg;
25108         
25109     },
25110     
25111     /**
25112      * return the real input element.
25113      */
25114     inputEl: function ()
25115     {
25116         return this.el.select('input.roo-' + this.inputType,true).first();
25117     },
25118     hiddenEl: function ()
25119     {
25120         return this.el.select('input.roo-hidden-value',true).first();
25121     },
25122     
25123     labelEl: function()
25124     {
25125         return this.el.select('label.control-label',true).first();
25126     },
25127     /* depricated... */
25128     
25129     label: function()
25130     {
25131         return this.labelEl();
25132     },
25133     
25134     boxLabelEl: function()
25135     {
25136         return this.el.select('label.box-label',true).first();
25137     },
25138     
25139     initEvents : function()
25140     {
25141 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25142         
25143         this.inputEl().on('click', this.onClick,  this);
25144         
25145         if (this.boxLabel) { 
25146             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25147         }
25148         
25149         this.startValue = this.getValue();
25150         
25151         if(this.groupId){
25152             Roo.bootstrap.form.CheckBox.register(this);
25153         }
25154     },
25155     
25156     onClick : function(e)
25157     {   
25158         if(this.fireEvent('click', this, e) !== false){
25159             this.setChecked(!this.checked);
25160         }
25161         
25162     },
25163     
25164     setChecked : function(state,suppressEvent)
25165     {
25166         this.startValue = this.getValue();
25167
25168         if(this.inputType == 'radio'){
25169             
25170             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171                 e.dom.checked = false;
25172             });
25173             
25174             this.inputEl().dom.checked = true;
25175             
25176             this.inputEl().dom.value = this.inputValue;
25177             
25178             if(suppressEvent !== true){
25179                 this.fireEvent('check', this, true);
25180             }
25181             
25182             this.validate();
25183             
25184             return;
25185         }
25186         
25187         this.checked = state;
25188         
25189         this.inputEl().dom.checked = state;
25190         
25191         
25192         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25193         
25194         if(suppressEvent !== true){
25195             this.fireEvent('check', this, state);
25196         }
25197         
25198         this.validate();
25199     },
25200     
25201     getValue : function()
25202     {
25203         if(this.inputType == 'radio'){
25204             return this.getGroupValue();
25205         }
25206         
25207         return this.hiddenEl().dom.value;
25208         
25209     },
25210     
25211     getGroupValue : function()
25212     {
25213         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25214             return '';
25215         }
25216         
25217         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25218     },
25219     
25220     setValue : function(v,suppressEvent)
25221     {
25222         if(this.inputType == 'radio'){
25223             this.setGroupValue(v, suppressEvent);
25224             return;
25225         }
25226         
25227         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25228         
25229         this.validate();
25230     },
25231     
25232     setGroupValue : function(v, suppressEvent)
25233     {
25234         this.startValue = this.getValue();
25235         
25236         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25237             e.dom.checked = false;
25238             
25239             if(e.dom.value == v){
25240                 e.dom.checked = true;
25241             }
25242         });
25243         
25244         if(suppressEvent !== true){
25245             this.fireEvent('check', this, true);
25246         }
25247
25248         this.validate();
25249         
25250         return;
25251     },
25252     
25253     validate : function()
25254     {
25255         if(this.getVisibilityEl().hasClass('hidden')){
25256             return true;
25257         }
25258         
25259         if(
25260                 this.disabled || 
25261                 (this.inputType == 'radio' && this.validateRadio()) ||
25262                 (this.inputType == 'checkbox' && this.validateCheckbox())
25263         ){
25264             this.markValid();
25265             return true;
25266         }
25267         
25268         this.markInvalid();
25269         return false;
25270     },
25271     
25272     validateRadio : function()
25273     {
25274         if(this.getVisibilityEl().hasClass('hidden')){
25275             return true;
25276         }
25277         
25278         if(this.allowBlank){
25279             return true;
25280         }
25281         
25282         var valid = false;
25283         
25284         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25285             if(!e.dom.checked){
25286                 return;
25287             }
25288             
25289             valid = true;
25290             
25291             return false;
25292         });
25293         
25294         return valid;
25295     },
25296     
25297     validateCheckbox : function()
25298     {
25299         if(!this.groupId){
25300             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25301             //return (this.getValue() == this.inputValue) ? true : false;
25302         }
25303         
25304         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25305         
25306         if(!group){
25307             return false;
25308         }
25309         
25310         var r = false;
25311         
25312         for(var i in group){
25313             if(group[i].el.isVisible(true)){
25314                 r = false;
25315                 break;
25316             }
25317             
25318             r = true;
25319         }
25320         
25321         for(var i in group){
25322             if(r){
25323                 break;
25324             }
25325             
25326             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327         }
25328         
25329         return r;
25330     },
25331     
25332     /**
25333      * Mark this field as valid
25334      */
25335     markValid : function()
25336     {
25337         var _this = this;
25338         
25339         this.fireEvent('valid', this);
25340         
25341         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25342         
25343         if(this.groupId){
25344             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345         }
25346         
25347         if(label){
25348             label.markValid();
25349         }
25350
25351         if(this.inputType == 'radio'){
25352             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25353                 var fg = e.findParent('.form-group', false, true);
25354                 if (Roo.bootstrap.version == 3) {
25355                     fg.removeClass([_this.invalidClass, _this.validClass]);
25356                     fg.addClass(_this.validClass);
25357                 } else {
25358                     fg.removeClass(['is-valid', 'is-invalid']);
25359                     fg.addClass('is-valid');
25360                 }
25361             });
25362             
25363             return;
25364         }
25365
25366         if(!this.groupId){
25367             var fg = this.el.findParent('.form-group', false, true);
25368             if (Roo.bootstrap.version == 3) {
25369                 fg.removeClass([this.invalidClass, this.validClass]);
25370                 fg.addClass(this.validClass);
25371             } else {
25372                 fg.removeClass(['is-valid', 'is-invalid']);
25373                 fg.addClass('is-valid');
25374             }
25375             return;
25376         }
25377         
25378         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25379         
25380         if(!group){
25381             return;
25382         }
25383         
25384         for(var i in group){
25385             var fg = group[i].el.findParent('.form-group', false, true);
25386             if (Roo.bootstrap.version == 3) {
25387                 fg.removeClass([this.invalidClass, this.validClass]);
25388                 fg.addClass(this.validClass);
25389             } else {
25390                 fg.removeClass(['is-valid', 'is-invalid']);
25391                 fg.addClass('is-valid');
25392             }
25393         }
25394     },
25395     
25396      /**
25397      * Mark this field as invalid
25398      * @param {String} msg The validation message
25399      */
25400     markInvalid : function(msg)
25401     {
25402         if(this.allowBlank){
25403             return;
25404         }
25405         
25406         var _this = this;
25407         
25408         this.fireEvent('invalid', this, msg);
25409         
25410         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25411         
25412         if(this.groupId){
25413             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25414         }
25415         
25416         if(label){
25417             label.markInvalid();
25418         }
25419             
25420         if(this.inputType == 'radio'){
25421             
25422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25423                 var fg = e.findParent('.form-group', false, true);
25424                 if (Roo.bootstrap.version == 3) {
25425                     fg.removeClass([_this.invalidClass, _this.validClass]);
25426                     fg.addClass(_this.invalidClass);
25427                 } else {
25428                     fg.removeClass(['is-invalid', 'is-valid']);
25429                     fg.addClass('is-invalid');
25430                 }
25431             });
25432             
25433             return;
25434         }
25435         
25436         if(!this.groupId){
25437             var fg = this.el.findParent('.form-group', false, true);
25438             if (Roo.bootstrap.version == 3) {
25439                 fg.removeClass([_this.invalidClass, _this.validClass]);
25440                 fg.addClass(_this.invalidClass);
25441             } else {
25442                 fg.removeClass(['is-invalid', 'is-valid']);
25443                 fg.addClass('is-invalid');
25444             }
25445             return;
25446         }
25447         
25448         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25449         
25450         if(!group){
25451             return;
25452         }
25453         
25454         for(var i in group){
25455             var fg = group[i].el.findParent('.form-group', false, true);
25456             if (Roo.bootstrap.version == 3) {
25457                 fg.removeClass([_this.invalidClass, _this.validClass]);
25458                 fg.addClass(_this.invalidClass);
25459             } else {
25460                 fg.removeClass(['is-invalid', 'is-valid']);
25461                 fg.addClass('is-invalid');
25462             }
25463         }
25464         
25465     },
25466     
25467     clearInvalid : function()
25468     {
25469         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25470         
25471         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25472         
25473         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25474         
25475         if (label && label.iconEl) {
25476             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25477             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25478         }
25479     },
25480     
25481     disable : function()
25482     {
25483         if(this.inputType != 'radio'){
25484             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485             return;
25486         }
25487         
25488         var _this = this;
25489         
25490         if(this.rendered){
25491             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25492                 _this.getActionEl().addClass(this.disabledClass);
25493                 e.dom.disabled = true;
25494             });
25495         }
25496         
25497         this.disabled = true;
25498         this.fireEvent("disable", this);
25499         return this;
25500     },
25501
25502     enable : function()
25503     {
25504         if(this.inputType != 'radio'){
25505             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506             return;
25507         }
25508         
25509         var _this = this;
25510         
25511         if(this.rendered){
25512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25513                 _this.getActionEl().removeClass(this.disabledClass);
25514                 e.dom.disabled = false;
25515             });
25516         }
25517         
25518         this.disabled = false;
25519         this.fireEvent("enable", this);
25520         return this;
25521     },
25522     
25523     setBoxLabel : function(v)
25524     {
25525         this.boxLabel = v;
25526         
25527         if(this.rendered){
25528             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25529         }
25530     }
25531
25532 });
25533
25534 Roo.apply(Roo.bootstrap.form.CheckBox, {
25535     
25536     groups: {},
25537     
25538      /**
25539     * register a CheckBox Group
25540     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25541     */
25542     register : function(checkbox)
25543     {
25544         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25545             this.groups[checkbox.groupId] = {};
25546         }
25547         
25548         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25549             return;
25550         }
25551         
25552         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25553         
25554     },
25555     /**
25556     * fetch a CheckBox Group based on the group ID
25557     * @param {string} the group ID
25558     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25559     */
25560     get: function(groupId) {
25561         if (typeof(this.groups[groupId]) == 'undefined') {
25562             return false;
25563         }
25564         
25565         return this.groups[groupId] ;
25566     }
25567     
25568     
25569 });
25570 /*
25571  * - LGPL
25572  *
25573  * RadioItem
25574  * 
25575  */
25576
25577 /**
25578  * @class Roo.bootstrap.form.Radio
25579  * @extends Roo.bootstrap.Component
25580  * Bootstrap Radio class
25581  * @cfg {String} boxLabel - the label associated
25582  * @cfg {String} value - the value of radio
25583  * 
25584  * @constructor
25585  * Create a new Radio
25586  * @param {Object} config The config object
25587  */
25588 Roo.bootstrap.form.Radio = function(config){
25589     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25590     
25591 };
25592
25593 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25594     
25595     boxLabel : '',
25596     
25597     value : '',
25598     
25599     getAutoCreate : function()
25600     {
25601         var cfg = {
25602             tag : 'div',
25603             cls : 'form-group radio',
25604             cn : [
25605                 {
25606                     tag : 'label',
25607                     cls : 'box-label',
25608                     html : this.boxLabel
25609                 }
25610             ]
25611         };
25612         
25613         return cfg;
25614     },
25615     
25616     initEvents : function() 
25617     {
25618         this.parent().register(this);
25619         
25620         this.el.on('click', this.onClick, this);
25621         
25622     },
25623     
25624     onClick : function(e)
25625     {
25626         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25627             this.setChecked(true);
25628         }
25629     },
25630     
25631     setChecked : function(state, suppressEvent)
25632     {
25633         this.parent().setValue(this.value, suppressEvent);
25634         
25635     },
25636     
25637     setBoxLabel : function(v)
25638     {
25639         this.boxLabel = v;
25640         
25641         if(this.rendered){
25642             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25643         }
25644     }
25645     
25646 });
25647  
25648
25649  /*
25650  * - LGPL
25651  *
25652  * Input
25653  * 
25654  */
25655
25656 /**
25657  * @class Roo.bootstrap.form.SecurePass
25658  * @extends Roo.bootstrap.form.Input
25659  * Bootstrap SecurePass class
25660  *
25661  * 
25662  * @constructor
25663  * Create a new SecurePass
25664  * @param {Object} config The config object
25665  */
25666  
25667 Roo.bootstrap.form.SecurePass = function (config) {
25668     // these go here, so the translation tool can replace them..
25669     this.errors = {
25670         PwdEmpty: "Please type a password, and then retype it to confirm.",
25671         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25672         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25673         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25674         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25675         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25676         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25677         TooWeak: "Your password is Too Weak."
25678     },
25679     this.meterLabel = "Password strength:";
25680     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25681     this.meterClass = [
25682         "roo-password-meter-tooweak", 
25683         "roo-password-meter-weak", 
25684         "roo-password-meter-medium", 
25685         "roo-password-meter-strong", 
25686         "roo-password-meter-grey"
25687     ];
25688     
25689     this.errors = {};
25690     
25691     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25692 }
25693
25694 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25695     /**
25696      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25697      * {
25698      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25699      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25700      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25701      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25702      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25703      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25704      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25705      * })
25706      */
25707     // private
25708     
25709     meterWidth: 300,
25710     errorMsg :'',    
25711     errors: false,
25712     imageRoot: '/',
25713     /**
25714      * @cfg {String/Object} Label for the strength meter (defaults to
25715      * 'Password strength:')
25716      */
25717     // private
25718     meterLabel: '',
25719     /**
25720      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25721      * ['Weak', 'Medium', 'Strong'])
25722      */
25723     // private    
25724     pwdStrengths: false,    
25725     // private
25726     strength: 0,
25727     // private
25728     _lastPwd: null,
25729     // private
25730     kCapitalLetter: 0,
25731     kSmallLetter: 1,
25732     kDigit: 2,
25733     kPunctuation: 3,
25734     
25735     insecure: false,
25736     // private
25737     initEvents: function ()
25738     {
25739         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25740
25741         if (this.el.is('input[type=password]') && Roo.isSafari) {
25742             this.el.on('keydown', this.SafariOnKeyDown, this);
25743         }
25744
25745         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25746     },
25747     // private
25748     onRender: function (ct, position)
25749     {
25750         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25751         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25752         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25753
25754         this.trigger.createChild({
25755                    cn: [
25756                     {
25757                     //id: 'PwdMeter',
25758                     tag: 'div',
25759                     cls: 'roo-password-meter-grey col-xs-12',
25760                     style: {
25761                         //width: 0,
25762                         //width: this.meterWidth + 'px'                                                
25763                         }
25764                     },
25765                     {                            
25766                          cls: 'roo-password-meter-text'                          
25767                     }
25768                 ]            
25769         });
25770
25771          
25772         if (this.hideTrigger) {
25773             this.trigger.setDisplayed(false);
25774         }
25775         this.setSize(this.width || '', this.height || '');
25776     },
25777     // private
25778     onDestroy: function ()
25779     {
25780         if (this.trigger) {
25781             this.trigger.removeAllListeners();
25782             this.trigger.remove();
25783         }
25784         if (this.wrap) {
25785             this.wrap.remove();
25786         }
25787         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25788     },
25789     // private
25790     checkStrength: function ()
25791     {
25792         var pwd = this.inputEl().getValue();
25793         if (pwd == this._lastPwd) {
25794             return;
25795         }
25796
25797         var strength;
25798         if (this.ClientSideStrongPassword(pwd)) {
25799             strength = 3;
25800         } else if (this.ClientSideMediumPassword(pwd)) {
25801             strength = 2;
25802         } else if (this.ClientSideWeakPassword(pwd)) {
25803             strength = 1;
25804         } else {
25805             strength = 0;
25806         }
25807         
25808         Roo.log('strength1: ' + strength);
25809         
25810         //var pm = this.trigger.child('div/div/div').dom;
25811         var pm = this.trigger.child('div/div');
25812         pm.removeClass(this.meterClass);
25813         pm.addClass(this.meterClass[strength]);
25814                 
25815         
25816         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25817                 
25818         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25819         
25820         this._lastPwd = pwd;
25821     },
25822     reset: function ()
25823     {
25824         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25825         
25826         this._lastPwd = '';
25827         
25828         var pm = this.trigger.child('div/div');
25829         pm.removeClass(this.meterClass);
25830         pm.addClass('roo-password-meter-grey');        
25831         
25832         
25833         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25834         
25835         pt.innerHTML = '';
25836         this.inputEl().dom.type='password';
25837     },
25838     // private
25839     validateValue: function (value)
25840     {
25841         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25842             return false;
25843         }
25844         if (value.length == 0) {
25845             if (this.allowBlank) {
25846                 this.clearInvalid();
25847                 return true;
25848             }
25849
25850             this.markInvalid(this.errors.PwdEmpty);
25851             this.errorMsg = this.errors.PwdEmpty;
25852             return false;
25853         }
25854         
25855         if(this.insecure){
25856             return true;
25857         }
25858         
25859         if (!value.match(/[\x21-\x7e]+/)) {
25860             this.markInvalid(this.errors.PwdBadChar);
25861             this.errorMsg = this.errors.PwdBadChar;
25862             return false;
25863         }
25864         if (value.length < 6) {
25865             this.markInvalid(this.errors.PwdShort);
25866             this.errorMsg = this.errors.PwdShort;
25867             return false;
25868         }
25869         if (value.length > 16) {
25870             this.markInvalid(this.errors.PwdLong);
25871             this.errorMsg = this.errors.PwdLong;
25872             return false;
25873         }
25874         var strength;
25875         if (this.ClientSideStrongPassword(value)) {
25876             strength = 3;
25877         } else if (this.ClientSideMediumPassword(value)) {
25878             strength = 2;
25879         } else if (this.ClientSideWeakPassword(value)) {
25880             strength = 1;
25881         } else {
25882             strength = 0;
25883         }
25884
25885         
25886         if (strength < 2) {
25887             //this.markInvalid(this.errors.TooWeak);
25888             this.errorMsg = this.errors.TooWeak;
25889             //return false;
25890         }
25891         
25892         
25893         console.log('strength2: ' + strength);
25894         
25895         //var pm = this.trigger.child('div/div/div').dom;
25896         
25897         var pm = this.trigger.child('div/div');
25898         pm.removeClass(this.meterClass);
25899         pm.addClass(this.meterClass[strength]);
25900                 
25901         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25902                 
25903         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25904         
25905         this.errorMsg = ''; 
25906         return true;
25907     },
25908     // private
25909     CharacterSetChecks: function (type)
25910     {
25911         this.type = type;
25912         this.fResult = false;
25913     },
25914     // private
25915     isctype: function (character, type)
25916     {
25917         switch (type) {  
25918             case this.kCapitalLetter:
25919                 if (character >= 'A' && character <= 'Z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kSmallLetter:
25925                 if (character >= 'a' && character <= 'z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kDigit:
25931                 if (character >= '0' && character <= '9') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kPunctuation:
25937                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             default:
25943                 return false;
25944         }
25945
25946     },
25947     // private
25948     IsLongEnough: function (pwd, size)
25949     {
25950         return !(pwd == null || isNaN(size) || pwd.length < size);
25951     },
25952     // private
25953     SpansEnoughCharacterSets: function (word, nb)
25954     {
25955         if (!this.IsLongEnough(word, nb))
25956         {
25957             return false;
25958         }
25959
25960         var characterSetChecks = new Array(
25961             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25962             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25963         );
25964         
25965         for (var index = 0; index < word.length; ++index) {
25966             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25967                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25968                     characterSetChecks[nCharSet].fResult = true;
25969                     break;
25970                 }
25971             }
25972         }
25973
25974         var nCharSets = 0;
25975         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976             if (characterSetChecks[nCharSet].fResult) {
25977                 ++nCharSets;
25978             }
25979         }
25980
25981         if (nCharSets < nb) {
25982             return false;
25983         }
25984         return true;
25985     },
25986     // private
25987     ClientSideStrongPassword: function (pwd)
25988     {
25989         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25990     },
25991     // private
25992     ClientSideMediumPassword: function (pwd)
25993     {
25994         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25995     },
25996     // private
25997     ClientSideWeakPassword: function (pwd)
25998     {
25999         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26000     }
26001           
26002 });
26003 Roo.htmleditor = {};
26004  
26005 /**
26006  * @class Roo.htmleditor.Filter
26007  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26008  * @cfg {DomElement} node The node to iterate and filter
26009  * @cfg {boolean|String|Array} tag Tags to replace 
26010  * @constructor
26011  * Create a new Filter.
26012  * @param {Object} config Configuration options
26013  */
26014
26015
26016
26017 Roo.htmleditor.Filter = function(cfg) {
26018     Roo.apply(this.cfg);
26019     // this does not actually call walk as it's really just a abstract class
26020 }
26021
26022
26023 Roo.htmleditor.Filter.prototype = {
26024     
26025     node: false,
26026     
26027     tag: false,
26028
26029     // overrride to do replace comments.
26030     replaceComment : false,
26031     
26032     // overrride to do replace or do stuff with tags..
26033     replaceTag : false,
26034     
26035     walk : function(dom)
26036     {
26037         Roo.each( Array.from(dom.childNodes), function( e ) {
26038             switch(true) {
26039                 
26040                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26041                     this.replaceComment(e);
26042                     return;
26043                 
26044                 case e.nodeType != 1: //not a node.
26045                     return;
26046                 
26047                 case this.tag === true: // everything
26048                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26049                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26050                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26051                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26052                     if (this.replaceTag && false === this.replaceTag(e)) {
26053                         return;
26054                     }
26055                     if (e.hasChildNodes()) {
26056                         this.walk(e);
26057                     }
26058                     return;
26059                 
26060                 default:    // tags .. that do not match.
26061                     if (e.hasChildNodes()) {
26062                         this.walk(e);
26063                     }
26064             }
26065             
26066         }, this);
26067         
26068     },
26069     
26070     
26071     removeNodeKeepChildren : function( node)
26072     {
26073     
26074         ar = Array.from(node.childNodes);
26075         for (var i = 0; i < ar.length; i++) {
26076          
26077             node.removeChild(ar[i]);
26078             // what if we need to walk these???
26079             node.parentNode.insertBefore(ar[i], node);
26080            
26081         }
26082         node.parentNode.removeChild(node);
26083     }
26084 }; 
26085
26086 /**
26087  * @class Roo.htmleditor.FilterAttributes
26088  * clean attributes and  styles including http:// etc.. in attribute
26089  * @constructor
26090 * Run a new Attribute Filter
26091 * @param {Object} config Configuration options
26092  */
26093 Roo.htmleditor.FilterAttributes = function(cfg)
26094 {
26095     Roo.apply(this, cfg);
26096     this.attrib_black = this.attrib_black || [];
26097     this.attrib_white = this.attrib_white || [];
26098
26099     this.attrib_clean = this.attrib_clean || [];
26100     this.style_white = this.style_white || [];
26101     this.style_black = this.style_black || [];
26102     this.walk(cfg.node);
26103 }
26104
26105 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26106 {
26107     tag: true, // all tags
26108     
26109     attrib_black : false, // array
26110     attrib_clean : false,
26111     attrib_white : false,
26112
26113     style_white : false,
26114     style_black : false,
26115      
26116      
26117     replaceTag : function(node)
26118     {
26119         if (!node.attributes || !node.attributes.length) {
26120             return true;
26121         }
26122         
26123         for (var i = node.attributes.length-1; i > -1 ; i--) {
26124             var a = node.attributes[i];
26125             //console.log(a);
26126             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26127                 node.removeAttribute(a.name);
26128                 continue;
26129             }
26130             
26131             
26132             
26133             if (a.name.toLowerCase().substr(0,2)=='on')  {
26134                 node.removeAttribute(a.name);
26135                 continue;
26136             }
26137             
26138             
26139             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26140                 node.removeAttribute(a.name);
26141                 continue;
26142             }
26143             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26144                 this.cleanAttr(node,a.name,a.value); // fixme..
26145                 continue;
26146             }
26147             if (a.name == 'style') {
26148                 this.cleanStyle(node,a.name,a.value);
26149                 continue;
26150             }
26151             /// clean up MS crap..
26152             // tecnically this should be a list of valid class'es..
26153             
26154             
26155             if (a.name == 'class') {
26156                 if (a.value.match(/^Mso/)) {
26157                     node.removeAttribute('class');
26158                 }
26159                 
26160                 if (a.value.match(/^body$/)) {
26161                     node.removeAttribute('class');
26162                 }
26163                 continue;
26164             }
26165             
26166             
26167             // style cleanup!?
26168             // class cleanup?
26169             
26170         }
26171         return true; // clean children
26172     },
26173         
26174     cleanAttr: function(node, n,v)
26175     {
26176         
26177         if (v.match(/^\./) || v.match(/^\//)) {
26178             return;
26179         }
26180         if (v.match(/^(http|https):\/\//)
26181             || v.match(/^mailto:/) 
26182             || v.match(/^ftp:/)
26183             || v.match(/^data:/)
26184             ) {
26185             return;
26186         }
26187         if (v.match(/^#/)) {
26188             return;
26189         }
26190         if (v.match(/^\{/)) { // allow template editing.
26191             return;
26192         }
26193 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26194         node.removeAttribute(n);
26195         
26196     },
26197     cleanStyle : function(node,  n,v)
26198     {
26199         if (v.match(/expression/)) { //XSS?? should we even bother..
26200             node.removeAttribute(n);
26201             return;
26202         }
26203         
26204         var parts = v.split(/;/);
26205         var clean = [];
26206         
26207         Roo.each(parts, function(p) {
26208             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26209             if (!p.length) {
26210                 return true;
26211             }
26212             var l = p.split(':').shift().replace(/\s+/g,'');
26213             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26214             
26215             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26216                 return true;
26217             }
26218             //Roo.log()
26219             // only allow 'c whitelisted system attributes'
26220             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26221                 return true;
26222             }
26223             
26224             
26225             clean.push(p);
26226             return true;
26227         },this);
26228         if (clean.length) { 
26229             node.setAttribute(n, clean.join(';'));
26230         } else {
26231             node.removeAttribute(n);
26232         }
26233         
26234     }
26235         
26236         
26237         
26238     
26239 });/**
26240  * @class Roo.htmleditor.FilterBlack
26241  * remove blacklisted elements.
26242  * @constructor
26243  * Run a new Blacklisted Filter
26244  * @param {Object} config Configuration options
26245  */
26246
26247 Roo.htmleditor.FilterBlack = function(cfg)
26248 {
26249     Roo.apply(this, cfg);
26250     this.walk(cfg.node);
26251 }
26252
26253 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26254 {
26255     tag : true, // all elements.
26256    
26257     replaceTag : function(n)
26258     {
26259         n.parentNode.removeChild(n);
26260     }
26261 });
26262 /**
26263  * @class Roo.htmleditor.FilterComment
26264  * remove comments.
26265  * @constructor
26266 * Run a new Comments Filter
26267 * @param {Object} config Configuration options
26268  */
26269 Roo.htmleditor.FilterComment = function(cfg)
26270 {
26271     this.walk(cfg.node);
26272 }
26273
26274 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26275 {
26276   
26277     replaceComment : function(n)
26278     {
26279         n.parentNode.removeChild(n);
26280     }
26281 });/**
26282  * @class Roo.htmleditor.FilterKeepChildren
26283  * remove tags but keep children
26284  * @constructor
26285  * Run a new Keep Children Filter
26286  * @param {Object} config Configuration options
26287  */
26288
26289 Roo.htmleditor.FilterKeepChildren = function(cfg)
26290 {
26291     Roo.apply(this, cfg);
26292     if (this.tag === false) {
26293         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26294     }
26295     // hacky?
26296     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26297         this.cleanNamespace = true;
26298     }
26299         
26300     this.walk(cfg.node);
26301 }
26302
26303 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26304 {
26305     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26306   
26307     replaceTag : function(node)
26308     {
26309         // walk children...
26310         //Roo.log(node.tagName);
26311         var ar = Array.from(node.childNodes);
26312         //remove first..
26313         
26314         for (var i = 0; i < ar.length; i++) {
26315             var e = ar[i];
26316             if (e.nodeType == 1) {
26317                 if (
26318                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26319                     || // array and it matches
26320                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26321                     ||
26322                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26323                     ||
26324                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26325                 ) {
26326                     this.replaceTag(ar[i]); // child is blacklisted as well...
26327                     continue;
26328                 }
26329             }
26330         }  
26331         ar = Array.from(node.childNodes);
26332         for (var i = 0; i < ar.length; i++) {
26333          
26334             node.removeChild(ar[i]);
26335             // what if we need to walk these???
26336             node.parentNode.insertBefore(ar[i], node);
26337             if (this.tag !== false) {
26338                 this.walk(ar[i]);
26339                 
26340             }
26341         }
26342         //Roo.log("REMOVE:" + node.tagName);
26343         node.parentNode.removeChild(node);
26344         return false; // don't walk children
26345         
26346         
26347     }
26348 });/**
26349  * @class Roo.htmleditor.FilterParagraph
26350  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26351  * like on 'push' to remove the <p> tags and replace them with line breaks.
26352  * @constructor
26353  * Run a new Paragraph Filter
26354  * @param {Object} config Configuration options
26355  */
26356
26357 Roo.htmleditor.FilterParagraph = function(cfg)
26358 {
26359     // no need to apply config.
26360     this.walk(cfg.node);
26361 }
26362
26363 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26364 {
26365     
26366      
26367     tag : 'P',
26368     
26369      
26370     replaceTag : function(node)
26371     {
26372         
26373         if (node.childNodes.length == 1 &&
26374             node.childNodes[0].nodeType == 3 &&
26375             node.childNodes[0].textContent.trim().length < 1
26376             ) {
26377             // remove and replace with '<BR>';
26378             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26379             return false; // no need to walk..
26380         }
26381         var ar = Array.from(node.childNodes);
26382         for (var i = 0; i < ar.length; i++) {
26383             node.removeChild(ar[i]);
26384             // what if we need to walk these???
26385             node.parentNode.insertBefore(ar[i], node);
26386         }
26387         // now what about this?
26388         // <p> &nbsp; </p>
26389         
26390         // double BR.
26391         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26392         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26393         node.parentNode.removeChild(node);
26394         
26395         return false;
26396
26397     }
26398     
26399 });/**
26400  * @class Roo.htmleditor.FilterSpan
26401  * filter span's with no attributes out..
26402  * @constructor
26403  * Run a new Span Filter
26404  * @param {Object} config Configuration options
26405  */
26406
26407 Roo.htmleditor.FilterSpan = function(cfg)
26408 {
26409     // no need to apply config.
26410     this.walk(cfg.node);
26411 }
26412
26413 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26414 {
26415      
26416     tag : 'SPAN',
26417      
26418  
26419     replaceTag : function(node)
26420     {
26421         if (node.attributes && node.attributes.length > 0) {
26422             return true; // walk if there are any.
26423         }
26424         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26425         return false;
26426      
26427     }
26428     
26429 });/**
26430  * @class Roo.htmleditor.FilterTableWidth
26431   try and remove table width data - as that frequently messes up other stuff.
26432  * 
26433  *      was cleanTableWidths.
26434  *
26435  * Quite often pasting from word etc.. results in tables with column and widths.
26436  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26437  *
26438  * @constructor
26439  * Run a new Table Filter
26440  * @param {Object} config Configuration options
26441  */
26442
26443 Roo.htmleditor.FilterTableWidth = function(cfg)
26444 {
26445     // no need to apply config.
26446     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26447     this.walk(cfg.node);
26448 }
26449
26450 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26451 {
26452      
26453      
26454     
26455     replaceTag: function(node) {
26456         
26457         
26458       
26459         if (node.hasAttribute('width')) {
26460             node.removeAttribute('width');
26461         }
26462         
26463          
26464         if (node.hasAttribute("style")) {
26465             // pretty basic...
26466             
26467             var styles = node.getAttribute("style").split(";");
26468             var nstyle = [];
26469             Roo.each(styles, function(s) {
26470                 if (!s.match(/:/)) {
26471                     return;
26472                 }
26473                 var kv = s.split(":");
26474                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26475                     return;
26476                 }
26477                 // what ever is left... we allow.
26478                 nstyle.push(s);
26479             });
26480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26481             if (!nstyle.length) {
26482                 node.removeAttribute('style');
26483             }
26484         }
26485         
26486         return true; // continue doing children..
26487     }
26488 });/**
26489  * @class Roo.htmleditor.FilterWord
26490  * try and clean up all the mess that Word generates.
26491  * 
26492  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26493  
26494  * @constructor
26495  * Run a new Span Filter
26496  * @param {Object} config Configuration options
26497  */
26498
26499 Roo.htmleditor.FilterWord = function(cfg)
26500 {
26501     // no need to apply config.
26502     this.replaceDocBullets(cfg.node);
26503     
26504     this.replaceAname(cfg.node);
26505     // this is disabled as the removal is done by other filters;
26506    // this.walk(cfg.node);
26507     
26508     
26509 }
26510
26511 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26512 {
26513     tag: true,
26514      
26515     
26516     /**
26517      * Clean up MS wordisms...
26518      */
26519     replaceTag : function(node)
26520     {
26521          
26522         // no idea what this does - span with text, replaceds with just text.
26523         if(
26524                 node.nodeName == 'SPAN' &&
26525                 !node.hasAttributes() &&
26526                 node.childNodes.length == 1 &&
26527                 node.firstChild.nodeName == "#text"  
26528         ) {
26529             var textNode = node.firstChild;
26530             node.removeChild(textNode);
26531             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26532                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26533             }
26534             node.parentNode.insertBefore(textNode, node);
26535             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26536                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26537             }
26538             
26539             node.parentNode.removeChild(node);
26540             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26541         }
26542         
26543    
26544         
26545         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26546             node.parentNode.removeChild(node);
26547             return false; // dont do chidlren
26548         }
26549         //Roo.log(node.tagName);
26550         // remove - but keep children..
26551         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26552             //Roo.log('-- removed');
26553             while (node.childNodes.length) {
26554                 var cn = node.childNodes[0];
26555                 node.removeChild(cn);
26556                 node.parentNode.insertBefore(cn, node);
26557                 // move node to parent - and clean it..
26558                 if (cn.nodeType == 1) {
26559                     this.replaceTag(cn);
26560                 }
26561                 
26562             }
26563             node.parentNode.removeChild(node);
26564             /// no need to iterate chidlren = it's got none..
26565             //this.iterateChildren(node, this.cleanWord);
26566             return false; // no need to iterate children.
26567         }
26568         // clean styles
26569         if (node.className.length) {
26570             
26571             var cn = node.className.split(/\W+/);
26572             var cna = [];
26573             Roo.each(cn, function(cls) {
26574                 if (cls.match(/Mso[a-zA-Z]+/)) {
26575                     return;
26576                 }
26577                 cna.push(cls);
26578             });
26579             node.className = cna.length ? cna.join(' ') : '';
26580             if (!cna.length) {
26581                 node.removeAttribute("class");
26582             }
26583         }
26584         
26585         if (node.hasAttribute("lang")) {
26586             node.removeAttribute("lang");
26587         }
26588         
26589         if (node.hasAttribute("style")) {
26590             
26591             var styles = node.getAttribute("style").split(";");
26592             var nstyle = [];
26593             Roo.each(styles, function(s) {
26594                 if (!s.match(/:/)) {
26595                     return;
26596                 }
26597                 var kv = s.split(":");
26598                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26599                     return;
26600                 }
26601                 // what ever is left... we allow.
26602                 nstyle.push(s);
26603             });
26604             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26605             if (!nstyle.length) {
26606                 node.removeAttribute('style');
26607             }
26608         }
26609         return true; // do children
26610         
26611         
26612         
26613     },
26614     
26615     styleToObject: function(node)
26616     {
26617         var styles = (node.getAttribute("style") || '').split(";");
26618         var ret = {};
26619         Roo.each(styles, function(s) {
26620             if (!s.match(/:/)) {
26621                 return;
26622             }
26623             var kv = s.split(":");
26624              
26625             // what ever is left... we allow.
26626             ret[kv[0].trim()] = kv[1];
26627         });
26628         return ret;
26629     },
26630     
26631     
26632     replaceAname : function (doc)
26633     {
26634         // replace all the a/name without..
26635         var aa = Array.from(doc.getElementsByTagName('a'));
26636         for (var i = 0; i  < aa.length; i++) {
26637             var a = aa[i];
26638             if (a.hasAttribute("name")) {
26639                 a.removeAttribute("name");
26640             }
26641             if (a.hasAttribute("href")) {
26642                 continue;
26643             }
26644             // reparent children.
26645             this.removeNodeKeepChildren(a);
26646             
26647         }
26648         
26649         
26650         
26651     },
26652
26653     
26654     
26655     replaceDocBullets : function(doc)
26656     {
26657         // this is a bit odd - but it appears some indents use ql-indent-1
26658          //Roo.log(doc.innerHTML);
26659         
26660         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26661         for( var i = 0; i < listpara.length; i ++) {
26662             listpara[i].className = "MsoListParagraph";
26663         }
26664         
26665         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26666         for( var i = 0; i < listpara.length; i ++) {
26667             listpara[i].className = "MsoListParagraph";
26668         }
26669         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26670         for( var i = 0; i < listpara.length; i ++) {
26671             listpara[i].className = "MsoListParagraph";
26672         }
26673         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26674         for( var i = 0; i < listpara.length; i ++) {
26675             listpara[i].className = "MsoListParagraph";
26676         }
26677         
26678         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26679         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26680         for( var i = 0; i < htwo.length; i ++) {
26681             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26682                 htwo[i].className = "MsoListParagraph";
26683             }
26684         }
26685         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26686         for( var i = 0; i < listpara.length; i ++) {
26687             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26688                 listpara[i].className = "MsoListParagraph";
26689             } else {
26690                 listpara[i].className = "MsoNormalx";
26691             }
26692         }
26693        
26694         listpara = doc.getElementsByClassName('MsoListParagraph');
26695         // Roo.log(doc.innerHTML);
26696         
26697         
26698         
26699         while(listpara.length) {
26700             
26701             this.replaceDocBullet(listpara.item(0));
26702         }
26703       
26704     },
26705     
26706      
26707     
26708     replaceDocBullet : function(p)
26709     {
26710         // gather all the siblings.
26711         var ns = p,
26712             parent = p.parentNode,
26713             doc = parent.ownerDocument,
26714             items = [];
26715             
26716         var listtype = 'ul';   
26717         while (ns) {
26718             if (ns.nodeType != 1) {
26719                 ns = ns.nextSibling;
26720                 continue;
26721             }
26722             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26723                 break;
26724             }
26725             var spans = ns.getElementsByTagName('span');
26726             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26727                 items.push(ns);
26728                 ns = ns.nextSibling;
26729                 has_list = true;
26730                 if (spans.length && spans[0].hasAttribute('style')) {
26731                     var  style = this.styleToObject(spans[0]);
26732                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26733                         listtype = 'ol';
26734                     }
26735                 }
26736                 
26737                 continue;
26738             }
26739             var spans = ns.getElementsByTagName('span');
26740             if (!spans.length) {
26741                 break;
26742             }
26743             var has_list  = false;
26744             for(var i = 0; i < spans.length; i++) {
26745                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26746                     has_list = true;
26747                     break;
26748                 }
26749             }
26750             if (!has_list) {
26751                 break;
26752             }
26753             items.push(ns);
26754             ns = ns.nextSibling;
26755             
26756             
26757         }
26758         if (!items.length) {
26759             ns.className = "";
26760             return;
26761         }
26762         
26763         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26764         parent.insertBefore(ul, p);
26765         var lvl = 0;
26766         var stack = [ ul ];
26767         var last_li = false;
26768         
26769         var margin_to_depth = {};
26770         max_margins = -1;
26771         
26772         items.forEach(function(n, ipos) {
26773             //Roo.log("got innertHMLT=" + n.innerHTML);
26774             
26775             var spans = n.getElementsByTagName('span');
26776             if (!spans.length) {
26777                 //Roo.log("No spans found");
26778                  
26779                 parent.removeChild(n);
26780                 
26781                 
26782                 return; // skip it...
26783             }
26784            
26785                 
26786             var num = 1;
26787             var style = {};
26788             for(var i = 0; i < spans.length; i++) {
26789             
26790                 style = this.styleToObject(spans[i]);
26791                 if (typeof(style['mso-list']) == 'undefined') {
26792                     continue;
26793                 }
26794                 if (listtype == 'ol') {
26795                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26796                 }
26797                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26798                 break;
26799             }
26800             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26801             style = this.styleToObject(n); // mo-list is from the parent node.
26802             if (typeof(style['mso-list']) == 'undefined') {
26803                 //Roo.log("parent is missing level");
26804                   
26805                 parent.removeChild(n);
26806                  
26807                 return;
26808             }
26809             
26810             var margin = style['margin-left'];
26811             if (typeof(margin_to_depth[margin]) == 'undefined') {
26812                 max_margins++;
26813                 margin_to_depth[margin] = max_margins;
26814             }
26815             nlvl = margin_to_depth[margin] ;
26816              
26817             if (nlvl > lvl) {
26818                 //new indent
26819                 var nul = doc.createElement(listtype); // what about number lists...
26820                 if (!last_li) {
26821                     last_li = doc.createElement('li');
26822                     stack[lvl].appendChild(last_li);
26823                 }
26824                 last_li.appendChild(nul);
26825                 stack[nlvl] = nul;
26826                 
26827             }
26828             lvl = nlvl;
26829             
26830             // not starting at 1..
26831             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26832                 stack[nlvl].setAttribute("start", num);
26833             }
26834             
26835             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26836             last_li = nli;
26837             nli.innerHTML = n.innerHTML;
26838             //Roo.log("innerHTML = " + n.innerHTML);
26839             parent.removeChild(n);
26840             
26841              
26842              
26843             
26844         },this);
26845         
26846         
26847         
26848         
26849     }
26850     
26851     
26852     
26853 });
26854 /**
26855  * @class Roo.htmleditor.FilterStyleToTag
26856  * part of the word stuff... - certain 'styles' should be converted to tags.
26857  * eg.
26858  *   font-weight: bold -> bold
26859  *   ?? super / subscrit etc..
26860  * 
26861  * @constructor
26862 * Run a new style to tag filter.
26863 * @param {Object} config Configuration options
26864  */
26865 Roo.htmleditor.FilterStyleToTag = function(cfg)
26866 {
26867     
26868     this.tags = {
26869         B  : [ 'fontWeight' , 'bold'],
26870         I :  [ 'fontStyle' , 'italic'],
26871         //pre :  [ 'font-style' , 'italic'],
26872         // h1.. h6 ?? font-size?
26873         SUP : [ 'verticalAlign' , 'super' ],
26874         SUB : [ 'verticalAlign' , 'sub' ]
26875         
26876         
26877     };
26878     
26879     Roo.apply(this, cfg);
26880      
26881     
26882     this.walk(cfg.node);
26883     
26884     
26885     
26886 }
26887
26888
26889 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26890 {
26891     tag: true, // all tags
26892     
26893     tags : false,
26894     
26895     
26896     replaceTag : function(node)
26897     {
26898         
26899         
26900         if (node.getAttribute("style") === null) {
26901             return true;
26902         }
26903         var inject = [];
26904         for (var k in this.tags) {
26905             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26906                 inject.push(k);
26907                 node.style.removeProperty(this.tags[k][0]);
26908             }
26909         }
26910         if (!inject.length) {
26911             return true; 
26912         }
26913         var cn = Array.from(node.childNodes);
26914         var nn = node;
26915         Roo.each(inject, function(t) {
26916             var nc = node.ownerDocument.createElement(t);
26917             nn.appendChild(nc);
26918             nn = nc;
26919         });
26920         for(var i = 0;i < cn.length;cn++) {
26921             node.removeChild(cn[i]);
26922             nn.appendChild(cn[i]);
26923         }
26924         return true /// iterate thru
26925     }
26926     
26927 })/**
26928  * @class Roo.htmleditor.FilterLongBr
26929  * BR/BR/BR - keep a maximum of 2...
26930  * @constructor
26931  * Run a new Long BR Filter
26932  * @param {Object} config Configuration options
26933  */
26934
26935 Roo.htmleditor.FilterLongBr = function(cfg)
26936 {
26937     // no need to apply config.
26938     this.walk(cfg.node);
26939 }
26940
26941 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26942 {
26943     
26944      
26945     tag : 'BR',
26946     
26947      
26948     replaceTag : function(node)
26949     {
26950         
26951         var ps = node.nextSibling;
26952         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26953             ps = ps.nextSibling;
26954         }
26955         
26956         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26957             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26958             return false;
26959         }
26960         
26961         if (!ps || ps.nodeType != 1) {
26962             return false;
26963         }
26964         
26965         if (!ps || ps.tagName != 'BR') {
26966            
26967             return false;
26968         }
26969         
26970         
26971         
26972         
26973         
26974         if (!node.previousSibling) {
26975             return false;
26976         }
26977         var ps = node.previousSibling;
26978         
26979         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26980             ps = ps.previousSibling;
26981         }
26982         if (!ps || ps.nodeType != 1) {
26983             return false;
26984         }
26985         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26986         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26987             return false;
26988         }
26989         
26990         node.parentNode.removeChild(node); // remove me...
26991         
26992         return false; // no need to do children
26993
26994     }
26995     
26996 }); 
26997
26998 /**
26999  * @class Roo.htmleditor.FilterBlock
27000  * removes id / data-block and contenteditable that are associated with blocks
27001  * usage should be done on a cloned copy of the dom
27002  * @constructor
27003 * Run a new Attribute Filter { node : xxxx }}
27004 * @param {Object} config Configuration options
27005  */
27006 Roo.htmleditor.FilterBlock = function(cfg)
27007 {
27008     Roo.apply(this, cfg);
27009     var qa = cfg.node.querySelectorAll;
27010     this.removeAttributes('data-block');
27011     this.removeAttributes('contenteditable');
27012     this.removeAttributes('id');
27013     
27014 }
27015
27016 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27017 {
27018     node: true, // all tags
27019      
27020      
27021     removeAttributes : function(attr)
27022     {
27023         var ar = this.node.querySelectorAll('*[' + attr + ']');
27024         for (var i =0;i<ar.length;i++) {
27025             ar[i].removeAttribute(attr);
27026         }
27027     }
27028         
27029         
27030         
27031     
27032 });
27033 /***
27034  * This is based loosely on tinymce 
27035  * @class Roo.htmleditor.TidySerializer
27036  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27037  * @constructor
27038  * @method Serializer
27039  * @param {Object} settings Name/value settings object.
27040  */
27041
27042
27043 Roo.htmleditor.TidySerializer = function(settings)
27044 {
27045     Roo.apply(this, settings);
27046     
27047     this.writer = new Roo.htmleditor.TidyWriter(settings);
27048     
27049     
27050
27051 };
27052 Roo.htmleditor.TidySerializer.prototype = {
27053     
27054     /**
27055      * @param {boolean} inner do the inner of the node.
27056      */
27057     inner : false,
27058     
27059     writer : false,
27060     
27061     /**
27062     * Serializes the specified node into a string.
27063     *
27064     * @example
27065     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27066     * @method serialize
27067     * @param {DomElement} node Node instance to serialize.
27068     * @return {String} String with HTML based on DOM tree.
27069     */
27070     serialize : function(node) {
27071         
27072         // = settings.validate;
27073         var writer = this.writer;
27074         var self  = this;
27075         this.handlers = {
27076             // #text
27077             3: function(node) {
27078                 
27079                 writer.text(node.nodeValue, node);
27080             },
27081             // #comment
27082             8: function(node) {
27083                 writer.comment(node.nodeValue);
27084             },
27085             // Processing instruction
27086             7: function(node) {
27087                 writer.pi(node.name, node.nodeValue);
27088             },
27089             // Doctype
27090             10: function(node) {
27091                 writer.doctype(node.nodeValue);
27092             },
27093             // CDATA
27094             4: function(node) {
27095                 writer.cdata(node.nodeValue);
27096             },
27097             // Document fragment
27098             11: function(node) {
27099                 node = node.firstChild;
27100                 if (!node) {
27101                     return;
27102                 }
27103                 while(node) {
27104                     self.walk(node);
27105                     node = node.nextSibling
27106                 }
27107             }
27108         };
27109         writer.reset();
27110         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27111         return writer.getContent();
27112     },
27113
27114     walk: function(node)
27115     {
27116         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27117             handler = this.handlers[node.nodeType];
27118             
27119         if (handler) {
27120             handler(node);
27121             return;
27122         }
27123     
27124         var name = node.nodeName;
27125         var isEmpty = node.childNodes.length < 1;
27126       
27127         var writer = this.writer;
27128         var attrs = node.attributes;
27129         // Sort attributes
27130         
27131         writer.start(node.nodeName, attrs, isEmpty, node);
27132         if (isEmpty) {
27133             return;
27134         }
27135         node = node.firstChild;
27136         if (!node) {
27137             writer.end(name);
27138             return;
27139         }
27140         while (node) {
27141             this.walk(node);
27142             node = node.nextSibling;
27143         }
27144         writer.end(name);
27145         
27146     
27147     }
27148     // Serialize element and treat all non elements as fragments
27149    
27150 }; 
27151
27152 /***
27153  * This is based loosely on tinymce 
27154  * @class Roo.htmleditor.TidyWriter
27155  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27156  *
27157  * Known issues?
27158  * - not tested much with 'PRE' formated elements.
27159  * 
27160  *
27161  *
27162  */
27163
27164 Roo.htmleditor.TidyWriter = function(settings)
27165 {
27166     
27167     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27168     Roo.apply(this, settings);
27169     this.html = [];
27170     this.state = [];
27171      
27172     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27173   
27174 }
27175 Roo.htmleditor.TidyWriter.prototype = {
27176
27177  
27178     state : false,
27179     
27180     indent :  '  ',
27181     
27182     // part of state...
27183     indentstr : '',
27184     in_pre: false,
27185     in_inline : false,
27186     last_inline : false,
27187     encode : false,
27188      
27189     
27190             /**
27191     * Writes the a start element such as <p id="a">.
27192     *
27193     * @method start
27194     * @param {String} name Name of the element.
27195     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27196     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27197     */
27198     start: function(name, attrs, empty, node)
27199     {
27200         var i, l, attr, value;
27201         
27202         // there are some situations where adding line break && indentation will not work. will not work.
27203         // <span / b / i ... formating?
27204         
27205         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27206         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27207         
27208         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27209         
27210         var add_lb = name == 'BR' ? false : in_inline;
27211         
27212         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27213             i_inline = false;
27214         }
27215
27216         var indentstr =  this.indentstr;
27217         
27218         // e_inline = elements that can be inline, but still allow \n before and after?
27219         // only 'BR' ??? any others?
27220         
27221         // ADD LINE BEFORE tage
27222         if (!this.in_pre) {
27223             if (in_inline) {
27224                 //code
27225                 if (name == 'BR') {
27226                     this.addLine();
27227                 } else if (this.lastElementEndsWS()) {
27228                     this.addLine();
27229                 } else{
27230                     // otherwise - no new line. (and dont indent.)
27231                     indentstr = '';
27232                 }
27233                 
27234             } else {
27235                 this.addLine();
27236             }
27237         } else {
27238             indentstr = '';
27239         }
27240         
27241         this.html.push(indentstr + '<', name.toLowerCase());
27242         
27243         if (attrs) {
27244             for (i = 0, l = attrs.length; i < l; i++) {
27245                 attr = attrs[i];
27246                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27247             }
27248         }
27249      
27250         if (empty) {
27251             if (is_short) {
27252                 this.html[this.html.length] = '/>';
27253             } else {
27254                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27255             }
27256             var e_inline = name == 'BR' ? false : this.in_inline;
27257             
27258             if (!e_inline && !this.in_pre) {
27259                 this.addLine();
27260             }
27261             return;
27262         
27263         }
27264         // not empty..
27265         this.html[this.html.length] = '>';
27266         
27267         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27268         /*
27269         if (!in_inline && !in_pre) {
27270             var cn = node.firstChild;
27271             while(cn) {
27272                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27273                     in_inline = true
27274                     break;
27275                 }
27276                 cn = cn.nextSibling;
27277             }
27278              
27279         }
27280         */
27281         
27282         
27283         this.pushState({
27284             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27285             in_pre : in_pre,
27286             in_inline :  in_inline
27287         });
27288         // add a line after if we are not in a
27289         
27290         if (!in_inline && !in_pre) {
27291             this.addLine();
27292         }
27293         
27294             
27295          
27296         
27297     },
27298     
27299     lastElementEndsWS : function()
27300     {
27301         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27302         if (value === false) {
27303             return true;
27304         }
27305         return value.match(/\s+$/);
27306         
27307     },
27308     
27309     /**
27310      * Writes the a end element such as </p>.
27311      *
27312      * @method end
27313      * @param {String} name Name of the element.
27314      */
27315     end: function(name) {
27316         var value;
27317         this.popState();
27318         var indentstr = '';
27319         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27320         
27321         if (!this.in_pre && !in_inline) {
27322             this.addLine();
27323             indentstr  = this.indentstr;
27324         }
27325         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27326         this.last_inline = in_inline;
27327         
27328         // pop the indent state..
27329     },
27330     /**
27331      * Writes a text node.
27332      *
27333      * In pre - we should not mess with the contents.
27334      * 
27335      *
27336      * @method text
27337      * @param {String} text String to write out.
27338      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27339      */
27340     text: function(in_text, node)
27341     {
27342         // if not in whitespace critical
27343         if (in_text.length < 1) {
27344             return;
27345         }
27346         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27347         
27348         if (this.in_pre) {
27349             this.html[this.html.length] =  text;
27350             return;   
27351         }
27352         
27353         if (this.in_inline) {
27354             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27355             if (text != ' ') {
27356                 text = text.replace(/\s+/,' ');  // all white space to single white space
27357                 
27358                     
27359                 // if next tag is '<BR>', then we can trim right..
27360                 if (node.nextSibling &&
27361                     node.nextSibling.nodeType == 1 &&
27362                     node.nextSibling.nodeName == 'BR' )
27363                 {
27364                     text = text.replace(/\s+$/g,'');
27365                 }
27366                 // if previous tag was a BR, we can also trim..
27367                 if (node.previousSibling &&
27368                     node.previousSibling.nodeType == 1 &&
27369                     node.previousSibling.nodeName == 'BR' )
27370                 {
27371                     text = this.indentstr +  text.replace(/^\s+/g,'');
27372                 }
27373                 if (text.match(/\n/)) {
27374                     text = text.replace(
27375                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27376                     );
27377                     // remoeve the last whitespace / line break.
27378                     text = text.replace(/\n\s+$/,'');
27379                 }
27380                 // repace long lines
27381                 
27382             }
27383              
27384             this.html[this.html.length] =  text;
27385             return;   
27386         }
27387         // see if previous element was a inline element.
27388         var indentstr = this.indentstr;
27389    
27390         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27391         
27392         // should trim left?
27393         if (node.previousSibling &&
27394             node.previousSibling.nodeType == 1 &&
27395             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27396         {
27397             indentstr = '';
27398             
27399         } else {
27400             this.addLine();
27401             text = text.replace(/^\s+/,''); // trim left
27402           
27403         }
27404         // should trim right?
27405         if (node.nextSibling &&
27406             node.nextSibling.nodeType == 1 &&
27407             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27408         {
27409           // noop
27410             
27411         }  else {
27412             text = text.replace(/\s+$/,''); // trim right
27413         }
27414          
27415               
27416         
27417         
27418         
27419         if (text.length < 1) {
27420             return;
27421         }
27422         if (!text.match(/\n/)) {
27423             this.html.push(indentstr + text);
27424             return;
27425         }
27426         
27427         text = this.indentstr + text.replace(
27428             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27429         );
27430         // remoeve the last whitespace / line break.
27431         text = text.replace(/\s+$/,''); 
27432         
27433         this.html.push(text);
27434         
27435         // split and indent..
27436         
27437         
27438     },
27439     /**
27440      * Writes a cdata node such as <![CDATA[data]]>.
27441      *
27442      * @method cdata
27443      * @param {String} text String to write out inside the cdata.
27444      */
27445     cdata: function(text) {
27446         this.html.push('<![CDATA[', text, ']]>');
27447     },
27448     /**
27449     * Writes a comment node such as <!-- Comment -->.
27450     *
27451     * @method cdata
27452     * @param {String} text String to write out inside the comment.
27453     */
27454    comment: function(text) {
27455        this.html.push('<!--', text, '-->');
27456    },
27457     /**
27458      * Writes a PI node such as <?xml attr="value" ?>.
27459      *
27460      * @method pi
27461      * @param {String} name Name of the pi.
27462      * @param {String} text String to write out inside the pi.
27463      */
27464     pi: function(name, text) {
27465         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27466         this.indent != '' && this.html.push('\n');
27467     },
27468     /**
27469      * Writes a doctype node such as <!DOCTYPE data>.
27470      *
27471      * @method doctype
27472      * @param {String} text String to write out inside the doctype.
27473      */
27474     doctype: function(text) {
27475         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27476     },
27477     /**
27478      * Resets the internal buffer if one wants to reuse the writer.
27479      *
27480      * @method reset
27481      */
27482     reset: function() {
27483         this.html.length = 0;
27484         this.state = [];
27485         this.pushState({
27486             indentstr : '',
27487             in_pre : false, 
27488             in_inline : false
27489         })
27490     },
27491     /**
27492      * Returns the contents that got serialized.
27493      *
27494      * @method getContent
27495      * @return {String} HTML contents that got written down.
27496      */
27497     getContent: function() {
27498         return this.html.join('').replace(/\n$/, '');
27499     },
27500     
27501     pushState : function(cfg)
27502     {
27503         this.state.push(cfg);
27504         Roo.apply(this, cfg);
27505     },
27506     
27507     popState : function()
27508     {
27509         if (this.state.length < 1) {
27510             return; // nothing to push
27511         }
27512         var cfg = {
27513             in_pre: false,
27514             indentstr : ''
27515         };
27516         this.state.pop();
27517         if (this.state.length > 0) {
27518             cfg = this.state[this.state.length-1]; 
27519         }
27520         Roo.apply(this, cfg);
27521     },
27522     
27523     addLine: function()
27524     {
27525         if (this.html.length < 1) {
27526             return;
27527         }
27528         
27529         
27530         var value = this.html[this.html.length - 1];
27531         if (value.length > 0 && '\n' !== value) {
27532             this.html.push('\n');
27533         }
27534     }
27535     
27536     
27537 //'pre script noscript style textarea video audio iframe object code'
27538 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27539 // inline 
27540 };
27541
27542 Roo.htmleditor.TidyWriter.inline_elements = [
27543         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27544         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27545 ];
27546 Roo.htmleditor.TidyWriter.shortend_elements = [
27547     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27548     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27549 ];
27550
27551 Roo.htmleditor.TidyWriter.whitespace_elements = [
27552     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27553 ];
27554 /**
27555  * @class Roo.htmleditor.KeyEnter
27556  * Handle Enter press..
27557  * @cfg {Roo.HtmlEditorCore} core the editor.
27558  * @constructor
27559  * Create a new Filter.
27560  * @param {Object} config Configuration options
27561  */
27562
27563
27564
27565
27566
27567 Roo.htmleditor.KeyEnter = function(cfg) {
27568     Roo.apply(this, cfg);
27569     // this does not actually call walk as it's really just a abstract class
27570  
27571     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27572 }
27573
27574 //Roo.htmleditor.KeyEnter.i = 0;
27575
27576
27577 Roo.htmleditor.KeyEnter.prototype = {
27578     
27579     core : false,
27580     
27581     keypress : function(e)
27582     {
27583         if (e.charCode != 13 && e.charCode != 10) {
27584             Roo.log([e.charCode,e]);
27585             return true;
27586         }
27587         e.preventDefault();
27588         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27589         var doc = this.core.doc;
27590           //add a new line
27591        
27592     
27593         var sel = this.core.getSelection();
27594         var range = sel.getRangeAt(0);
27595         var n = range.commonAncestorContainer;
27596         var pc = range.closest([ 'ol', 'ul']);
27597         var pli = range.closest('li');
27598         if (!pc || e.ctrlKey) {
27599             // on it list, or ctrl pressed.
27600             if (!e.ctrlKey) {
27601                 sel.insertNode('br', 'after'); 
27602             } else {
27603                 // only do this if we have ctrl key..
27604                 var br = doc.createElement('br');
27605                 br.className = 'clear';
27606                 br.setAttribute('style', 'clear: both');
27607                 sel.insertNode(br, 'after'); 
27608             }
27609             
27610          
27611             this.core.undoManager.addEvent();
27612             this.core.fireEditorEvent(e);
27613             return false;
27614         }
27615         
27616         // deal with <li> insetion
27617         if (pli.innerText.trim() == '' &&
27618             pli.previousSibling &&
27619             pli.previousSibling.nodeName == 'LI' &&
27620             pli.previousSibling.innerText.trim() ==  '') {
27621             pli.parentNode.removeChild(pli.previousSibling);
27622             sel.cursorAfter(pc);
27623             this.core.undoManager.addEvent();
27624             this.core.fireEditorEvent(e);
27625             return false;
27626         }
27627     
27628         var li = doc.createElement('LI');
27629         li.innerHTML = '&nbsp;';
27630         if (!pli || !pli.firstSibling) {
27631             pc.appendChild(li);
27632         } else {
27633             pli.parentNode.insertBefore(li, pli.firstSibling);
27634         }
27635         sel.cursorText (li.firstChild);
27636       
27637         this.core.undoManager.addEvent();
27638         this.core.fireEditorEvent(e);
27639
27640         return false;
27641         
27642     
27643         
27644         
27645          
27646     }
27647 };
27648      
27649 /**
27650  * @class Roo.htmleditor.Block
27651  * Base class for html editor blocks - do not use it directly .. extend it..
27652  * @cfg {DomElement} node The node to apply stuff to.
27653  * @cfg {String} friendly_name the name that appears in the context bar about this block
27654  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27655  
27656  * @constructor
27657  * Create a new Filter.
27658  * @param {Object} config Configuration options
27659  */
27660
27661 Roo.htmleditor.Block  = function(cfg)
27662 {
27663     // do nothing .. should not be called really.
27664 }
27665 /**
27666  * factory method to get the block from an element (using cache if necessary)
27667  * @static
27668  * @param {HtmlElement} the dom element
27669  */
27670 Roo.htmleditor.Block.factory = function(node)
27671 {
27672     var cc = Roo.htmleditor.Block.cache;
27673     var id = Roo.get(node).id;
27674     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27675         Roo.htmleditor.Block.cache[id].readElement(node);
27676         return Roo.htmleditor.Block.cache[id];
27677     }
27678     var db  = node.getAttribute('data-block');
27679     if (!db) {
27680         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27681     }
27682     var cls = Roo.htmleditor['Block' + db];
27683     if (typeof(cls) == 'undefined') {
27684         //Roo.log(node.getAttribute('data-block'));
27685         Roo.log("OOps missing block : " + 'Block' + db);
27686         return false;
27687     }
27688     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27689     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27690 };
27691
27692 /**
27693  * initalize all Elements from content that are 'blockable'
27694  * @static
27695  * @param the body element
27696  */
27697 Roo.htmleditor.Block.initAll = function(body, type)
27698 {
27699     if (typeof(type) == 'undefined') {
27700         var ia = Roo.htmleditor.Block.initAll;
27701         ia(body,'table');
27702         ia(body,'td');
27703         ia(body,'figure');
27704         return;
27705     }
27706     Roo.each(Roo.get(body).query(type), function(e) {
27707         Roo.htmleditor.Block.factory(e);    
27708     },this);
27709 };
27710 // question goes here... do we need to clear out this cache sometimes?
27711 // or show we make it relivant to the htmleditor.
27712 Roo.htmleditor.Block.cache = {};
27713
27714 Roo.htmleditor.Block.prototype = {
27715     
27716     node : false,
27717     
27718      // used by context menu
27719     friendly_name : 'Based Block',
27720     
27721     // text for button to delete this element
27722     deleteTitle : false,
27723     
27724     context : false,
27725     /**
27726      * Update a node with values from this object
27727      * @param {DomElement} node
27728      */
27729     updateElement : function(node)
27730     {
27731         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27732     },
27733      /**
27734      * convert to plain HTML for calling insertAtCursor..
27735      */
27736     toHTML : function()
27737     {
27738         return Roo.DomHelper.markup(this.toObject());
27739     },
27740     /**
27741      * used by readEleemnt to extract data from a node
27742      * may need improving as it's pretty basic
27743      
27744      * @param {DomElement} node
27745      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27746      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27747      * @param {String} style the style property - eg. text-align
27748      */
27749     getVal : function(node, tag, attr, style)
27750     {
27751         var n = node;
27752         if (tag !== true && n.tagName != tag.toUpperCase()) {
27753             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27754             // but kiss for now.
27755             n = node.getElementsByTagName(tag).item(0);
27756         }
27757         if (!n) {
27758             return '';
27759         }
27760         if (attr === false) {
27761             return n;
27762         }
27763         if (attr == 'html') {
27764             return n.innerHTML;
27765         }
27766         if (attr == 'style') {
27767             return n.style[style]; 
27768         }
27769         
27770         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27771             
27772     },
27773     /**
27774      * create a DomHelper friendly object - for use with 
27775      * Roo.DomHelper.markup / overwrite / etc..
27776      * (override this)
27777      */
27778     toObject : function()
27779     {
27780         return {};
27781     },
27782       /**
27783      * Read a node that has a 'data-block' property - and extract the values from it.
27784      * @param {DomElement} node - the node
27785      */
27786     readElement : function(node)
27787     {
27788         
27789     } 
27790     
27791     
27792 };
27793
27794  
27795
27796 /**
27797  * @class Roo.htmleditor.BlockFigure
27798  * Block that has an image and a figcaption
27799  * @cfg {String} image_src the url for the image
27800  * @cfg {String} align (left|right) alignment for the block default left
27801  * @cfg {String} caption the text to appear below  (and in the alt tag)
27802  * @cfg {String} caption_display (block|none) display or not the caption
27803  * @cfg {String|number} image_width the width of the image number or %?
27804  * @cfg {String|number} image_height the height of the image number or %?
27805  * 
27806  * @constructor
27807  * Create a new Filter.
27808  * @param {Object} config Configuration options
27809  */
27810
27811 Roo.htmleditor.BlockFigure = function(cfg)
27812 {
27813     if (cfg.node) {
27814         this.readElement(cfg.node);
27815         this.updateElement(cfg.node);
27816     }
27817     Roo.apply(this, cfg);
27818 }
27819 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27820  
27821     
27822     // setable values.
27823     image_src: '',
27824     align: 'center',
27825     caption : '',
27826     caption_display : 'block',
27827     width : '100%',
27828     cls : '',
27829     href: '',
27830     video_url : '',
27831     
27832     // margin: '2%', not used
27833     
27834     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27835
27836     
27837     // used by context menu
27838     friendly_name : 'Image with caption',
27839     deleteTitle : "Delete Image and Caption",
27840     
27841     contextMenu : function(toolbar)
27842     {
27843         
27844         var block = function() {
27845             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27846         };
27847         
27848         
27849         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27850         
27851         var syncValue = toolbar.editorcore.syncValue;
27852         
27853         var fields = {};
27854         
27855         return [
27856              {
27857                 xtype : 'TextItem',
27858                 text : "Source: ",
27859                 xns : rooui.Toolbar  //Boostrap?
27860             },
27861             {
27862                 xtype : 'Button',
27863                 text: 'Change Image URL',
27864                  
27865                 listeners : {
27866                     click: function (btn, state)
27867                     {
27868                         var b = block();
27869                         
27870                         Roo.MessageBox.show({
27871                             title : "Image Source URL",
27872                             msg : "Enter the url for the image",
27873                             buttons: Roo.MessageBox.OKCANCEL,
27874                             fn: function(btn, val){
27875                                 if (btn != 'ok') {
27876                                     return;
27877                                 }
27878                                 b.image_src = val;
27879                                 b.updateElement();
27880                                 syncValue();
27881                                 toolbar.editorcore.onEditorEvent();
27882                             },
27883                             minWidth:250,
27884                             prompt:true,
27885                             //multiline: multiline,
27886                             modal : true,
27887                             value : b.image_src
27888                         });
27889                     }
27890                 },
27891                 xns : rooui.Toolbar
27892             },
27893          
27894             {
27895                 xtype : 'Button',
27896                 text: 'Change Link URL',
27897                  
27898                 listeners : {
27899                     click: function (btn, state)
27900                     {
27901                         var b = block();
27902                         
27903                         Roo.MessageBox.show({
27904                             title : "Link URL",
27905                             msg : "Enter the url for the link - leave blank to have no link",
27906                             buttons: Roo.MessageBox.OKCANCEL,
27907                             fn: function(btn, val){
27908                                 if (btn != 'ok') {
27909                                     return;
27910                                 }
27911                                 b.href = val;
27912                                 b.updateElement();
27913                                 syncValue();
27914                                 toolbar.editorcore.onEditorEvent();
27915                             },
27916                             minWidth:250,
27917                             prompt:true,
27918                             //multiline: multiline,
27919                             modal : true,
27920                             value : b.href
27921                         });
27922                     }
27923                 },
27924                 xns : rooui.Toolbar
27925             },
27926             {
27927                 xtype : 'Button',
27928                 text: 'Show Video URL',
27929                  
27930                 listeners : {
27931                     click: function (btn, state)
27932                     {
27933                         Roo.MessageBox.alert("Video URL",
27934                             block().video_url == '' ? 'This image is not linked ot a video' :
27935                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27936                     }
27937                 },
27938                 xns : rooui.Toolbar
27939             },
27940             
27941             
27942             {
27943                 xtype : 'TextItem',
27944                 text : "Width: ",
27945                 xns : rooui.Toolbar  //Boostrap?
27946             },
27947             {
27948                 xtype : 'ComboBox',
27949                 allowBlank : false,
27950                 displayField : 'val',
27951                 editable : true,
27952                 listWidth : 100,
27953                 triggerAction : 'all',
27954                 typeAhead : true,
27955                 valueField : 'val',
27956                 width : 70,
27957                 name : 'width',
27958                 listeners : {
27959                     select : function (combo, r, index)
27960                     {
27961                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27962                         var b = block();
27963                         b.width = r.get('val');
27964                         b.updateElement();
27965                         syncValue();
27966                         toolbar.editorcore.onEditorEvent();
27967                     }
27968                 },
27969                 xns : rooui.form,
27970                 store : {
27971                     xtype : 'SimpleStore',
27972                     data : [
27973                         ['100%'],
27974                         ['80%'],
27975                         ['50%'],
27976                         ['20%'],
27977                         ['10%']
27978                     ],
27979                     fields : [ 'val'],
27980                     xns : Roo.data
27981                 }
27982             },
27983             {
27984                 xtype : 'TextItem',
27985                 text : "Align: ",
27986                 xns : rooui.Toolbar  //Boostrap?
27987             },
27988             {
27989                 xtype : 'ComboBox',
27990                 allowBlank : false,
27991                 displayField : 'val',
27992                 editable : true,
27993                 listWidth : 100,
27994                 triggerAction : 'all',
27995                 typeAhead : true,
27996                 valueField : 'val',
27997                 width : 70,
27998                 name : 'align',
27999                 listeners : {
28000                     select : function (combo, r, index)
28001                     {
28002                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28003                         var b = block();
28004                         b.align = r.get('val');
28005                         b.updateElement();
28006                         syncValue();
28007                         toolbar.editorcore.onEditorEvent();
28008                     }
28009                 },
28010                 xns : rooui.form,
28011                 store : {
28012                     xtype : 'SimpleStore',
28013                     data : [
28014                         ['left'],
28015                         ['right'],
28016                         ['center']
28017                     ],
28018                     fields : [ 'val'],
28019                     xns : Roo.data
28020                 }
28021             },
28022             
28023             
28024             {
28025                 xtype : 'Button',
28026                 text: 'Hide Caption',
28027                 name : 'caption_display',
28028                 pressed : false,
28029                 enableToggle : true,
28030                 setValue : function(v) {
28031                     // this trigger toggle.
28032                      
28033                     this.setText(v ? "Hide Caption" : "Show Caption");
28034                     this.setPressed(v != 'block');
28035                 },
28036                 listeners : {
28037                     toggle: function (btn, state)
28038                     {
28039                         var b  = block();
28040                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28041                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28042                         b.updateElement();
28043                         syncValue();
28044                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28045                         toolbar.editorcore.onEditorEvent();
28046                     }
28047                 },
28048                 xns : rooui.Toolbar
28049             }
28050         ];
28051         
28052     },
28053     /**
28054      * create a DomHelper friendly object - for use with
28055      * Roo.DomHelper.markup / overwrite / etc..
28056      */
28057     toObject : function()
28058     {
28059         var d = document.createElement('div');
28060         d.innerHTML = this.caption;
28061         
28062         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28063         
28064         var iw = this.align == 'center' ? this.width : '100%';
28065         var img =   {
28066             tag : 'img',
28067             contenteditable : 'false',
28068             src : this.image_src,
28069             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28070             style: {
28071                 width : iw,
28072                 maxWidth : iw + ' !important', // this is not getting rendered?
28073                 margin : m  
28074                 
28075             }
28076         };
28077         /*
28078         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28079                     '<a href="{2}">' + 
28080                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28081                     '</a>' + 
28082                 '</div>',
28083         */
28084                 
28085         if (this.href.length > 0) {
28086             img = {
28087                 tag : 'a',
28088                 href: this.href,
28089                 contenteditable : 'true',
28090                 cn : [
28091                     img
28092                 ]
28093             };
28094         }
28095         
28096         
28097         if (this.video_url.length > 0) {
28098             img = {
28099                 tag : 'div',
28100                 cls : this.cls,
28101                 frameborder : 0,
28102                 allowfullscreen : true,
28103                 width : 420,  // these are for video tricks - that we replace the outer
28104                 height : 315,
28105                 src : this.video_url,
28106                 cn : [
28107                     img
28108                 ]
28109             };
28110         }
28111         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28112         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28113         
28114   
28115         var ret =   {
28116             tag: 'figure',
28117             'data-block' : 'Figure',
28118             'data-width' : this.width, 
28119             contenteditable : 'false',
28120             
28121             style : {
28122                 display: 'block',
28123                 float :  this.align ,
28124                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28125                 width : this.align == 'center' ? '100%' : this.width,
28126                 margin:  '0px',
28127                 padding: this.align == 'center' ? '0' : '0 10px' ,
28128                 textAlign : this.align   // seems to work for email..
28129                 
28130             },
28131            
28132             
28133             align : this.align,
28134             cn : [
28135                 img,
28136               
28137                 {
28138                     tag: 'figcaption',
28139                     'data-display' : this.caption_display,
28140                     style : {
28141                         textAlign : 'left',
28142                         fontSize : '16px',
28143                         lineHeight : '24px',
28144                         display : this.caption_display,
28145                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28146                         margin: m,
28147                         width: this.align == 'center' ?  this.width : '100%' 
28148                     
28149                          
28150                     },
28151                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28152                     cn : [
28153                         {
28154                             tag: 'div',
28155                             style  : {
28156                                 marginTop : '16px',
28157                                 textAlign : 'left'
28158                             },
28159                             align: 'left',
28160                             cn : [
28161                                 {
28162                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28163                                     tag : 'i',
28164                                     contenteditable : true,
28165                                     html : captionhtml
28166                                 }
28167                                 
28168                             ]
28169                         }
28170                         
28171                     ]
28172                     
28173                 }
28174             ]
28175         };
28176         return ret;
28177          
28178     },
28179     
28180     readElement : function(node)
28181     {
28182         // this should not really come from the link...
28183         this.video_url = this.getVal(node, 'div', 'src');
28184         this.cls = this.getVal(node, 'div', 'class');
28185         this.href = this.getVal(node, 'a', 'href');
28186         
28187         
28188         this.image_src = this.getVal(node, 'img', 'src');
28189          
28190         this.align = this.getVal(node, 'figure', 'align');
28191         var figcaption = this.getVal(node, 'figcaption', false);
28192         if (figcaption !== '') {
28193             this.caption = this.getVal(figcaption, 'i', 'html');
28194         }
28195         
28196
28197         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28198         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28199         this.width = this.getVal(node, true, 'data-width');
28200         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28201         
28202     },
28203     removeNode : function()
28204     {
28205         return this.node;
28206     }
28207     
28208   
28209    
28210      
28211     
28212     
28213     
28214     
28215 })
28216
28217  
28218
28219 /**
28220  * @class Roo.htmleditor.BlockTable
28221  * Block that manages a table
28222  * 
28223  * @constructor
28224  * Create a new Filter.
28225  * @param {Object} config Configuration options
28226  */
28227
28228 Roo.htmleditor.BlockTable = function(cfg)
28229 {
28230     if (cfg.node) {
28231         this.readElement(cfg.node);
28232         this.updateElement(cfg.node);
28233     }
28234     Roo.apply(this, cfg);
28235     if (!cfg.node) {
28236         this.rows = [];
28237         for(var r = 0; r < this.no_row; r++) {
28238             this.rows[r] = [];
28239             for(var c = 0; c < this.no_col; c++) {
28240                 this.rows[r][c] = this.emptyCell();
28241             }
28242         }
28243     }
28244     
28245     
28246 }
28247 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
28248  
28249     rows : false,
28250     no_col : 1,
28251     no_row : 1,
28252     
28253     
28254     width: '100%',
28255     
28256     // used by context menu
28257     friendly_name : 'Table',
28258     deleteTitle : 'Delete Table',
28259     // context menu is drawn once..
28260     
28261     contextMenu : function(toolbar)
28262     {
28263         
28264         var block = function() {
28265             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28266         };
28267         
28268         
28269         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28270         
28271         var syncValue = toolbar.editorcore.syncValue;
28272         
28273         var fields = {};
28274         
28275         return [
28276             {
28277                 xtype : 'TextItem',
28278                 text : "Width: ",
28279                 xns : rooui.Toolbar  //Boostrap?
28280             },
28281             {
28282                 xtype : 'ComboBox',
28283                 allowBlank : false,
28284                 displayField : 'val',
28285                 editable : true,
28286                 listWidth : 100,
28287                 triggerAction : 'all',
28288                 typeAhead : true,
28289                 valueField : 'val',
28290                 width : 100,
28291                 name : 'width',
28292                 listeners : {
28293                     select : function (combo, r, index)
28294                     {
28295                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28296                         var b = block();
28297                         b.width = r.get('val');
28298                         b.updateElement();
28299                         syncValue();
28300                         toolbar.editorcore.onEditorEvent();
28301                     }
28302                 },
28303                 xns : rooui.form,
28304                 store : {
28305                     xtype : 'SimpleStore',
28306                     data : [
28307                         ['100%'],
28308                         ['auto']
28309                     ],
28310                     fields : [ 'val'],
28311                     xns : Roo.data
28312                 }
28313             },
28314             // -------- Cols
28315             
28316             {
28317                 xtype : 'TextItem',
28318                 text : "Columns: ",
28319                 xns : rooui.Toolbar  //Boostrap?
28320             },
28321          
28322             {
28323                 xtype : 'Button',
28324                 text: '-',
28325                 listeners : {
28326                     click : function (_self, e)
28327                     {
28328                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28329                         block().removeColumn();
28330                         syncValue();
28331                         toolbar.editorcore.onEditorEvent();
28332                     }
28333                 },
28334                 xns : rooui.Toolbar
28335             },
28336             {
28337                 xtype : 'Button',
28338                 text: '+',
28339                 listeners : {
28340                     click : function (_self, e)
28341                     {
28342                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28343                         block().addColumn();
28344                         syncValue();
28345                         toolbar.editorcore.onEditorEvent();
28346                     }
28347                 },
28348                 xns : rooui.Toolbar
28349             },
28350             // -------- ROWS
28351             {
28352                 xtype : 'TextItem',
28353                 text : "Rows: ",
28354                 xns : rooui.Toolbar  //Boostrap?
28355             },
28356          
28357             {
28358                 xtype : 'Button',
28359                 text: '-',
28360                 listeners : {
28361                     click : function (_self, e)
28362                     {
28363                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28364                         block().removeRow();
28365                         syncValue();
28366                         toolbar.editorcore.onEditorEvent();
28367                     }
28368                 },
28369                 xns : rooui.Toolbar
28370             },
28371             {
28372                 xtype : 'Button',
28373                 text: '+',
28374                 listeners : {
28375                     click : function (_self, e)
28376                     {
28377                         block().addRow();
28378                         syncValue();
28379                         toolbar.editorcore.onEditorEvent();
28380                     }
28381                 },
28382                 xns : rooui.Toolbar
28383             },
28384             // -------- ROWS
28385             {
28386                 xtype : 'Button',
28387                 text: 'Reset Column Widths',
28388                 listeners : {
28389                     
28390                     click : function (_self, e)
28391                     {
28392                         block().resetWidths();
28393                         syncValue();
28394                         toolbar.editorcore.onEditorEvent();
28395                     }
28396                 },
28397                 xns : rooui.Toolbar
28398             } 
28399             
28400             
28401             
28402         ];
28403         
28404     },
28405     
28406     
28407   /**
28408      * create a DomHelper friendly object - for use with
28409      * Roo.DomHelper.markup / overwrite / etc..
28410      * ?? should it be called with option to hide all editing features?
28411      */
28412     toObject : function()
28413     {
28414         
28415         var ret = {
28416             tag : 'table',
28417             contenteditable : 'false', // this stops cell selection from picking the table.
28418             'data-block' : 'Table',
28419             style : {
28420                 width:  this.width,
28421                 border : 'solid 1px #000', // ??? hard coded?
28422                 'border-collapse' : 'collapse' 
28423             },
28424             cn : [
28425                 { tag : 'tbody' , cn : [] }
28426             ]
28427         };
28428         
28429         // do we have a head = not really 
28430         var ncols = 0;
28431         Roo.each(this.rows, function( row ) {
28432             var tr = {
28433                 tag: 'tr',
28434                 style : {
28435                     margin: '6px',
28436                     border : 'solid 1px #000',
28437                     textAlign : 'left' 
28438                 },
28439                 cn : [ ]
28440             };
28441             
28442             ret.cn[0].cn.push(tr);
28443             // does the row have any properties? ?? height?
28444             var nc = 0;
28445             Roo.each(row, function( cell ) {
28446                 
28447                 var td = {
28448                     tag : 'td',
28449                     contenteditable :  'true',
28450                     'data-block' : 'Td',
28451                     html : cell.html,
28452                     style : cell.style
28453                 };
28454                 if (cell.colspan > 1) {
28455                     td.colspan = cell.colspan ;
28456                     nc += cell.colspan;
28457                 } else {
28458                     nc++;
28459                 }
28460                 if (cell.rowspan > 1) {
28461                     td.rowspan = cell.rowspan ;
28462                 }
28463                 
28464                 
28465                 // widths ?
28466                 tr.cn.push(td);
28467                     
28468                 
28469             }, this);
28470             ncols = Math.max(nc, ncols);
28471             
28472             
28473         }, this);
28474         // add the header row..
28475         
28476         ncols++;
28477          
28478         
28479         return ret;
28480          
28481     },
28482     
28483     readElement : function(node)
28484     {
28485         node  = node ? node : this.node ;
28486         this.width = this.getVal(node, true, 'style', 'width') || '100%';
28487         
28488         this.rows = [];
28489         this.no_row = 0;
28490         var trs = Array.from(node.rows);
28491         trs.forEach(function(tr) {
28492             var row =  [];
28493             this.rows.push(row);
28494             
28495             this.no_row++;
28496             var no_column = 0;
28497             Array.from(tr.cells).forEach(function(td) {
28498                 
28499                 var add = {
28500                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
28501                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
28502                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
28503                     html : td.innerHTML
28504                 };
28505                 no_column += add.colspan;
28506                      
28507                 
28508                 row.push(add);
28509                 
28510                 
28511             },this);
28512             this.no_col = Math.max(this.no_col, no_column);
28513             
28514             
28515         },this);
28516         
28517         
28518     },
28519     normalizeRows: function()
28520     {
28521         var ret= [];
28522         var rid = -1;
28523         this.rows.forEach(function(row) {
28524             rid++;
28525             ret[rid] = [];
28526             row = this.normalizeRow(row);
28527             var cid = 0;
28528             row.forEach(function(c) {
28529                 while (typeof(ret[rid][cid]) != 'undefined') {
28530                     cid++;
28531                 }
28532                 if (typeof(ret[rid]) == 'undefined') {
28533                     ret[rid] = [];
28534                 }
28535                 ret[rid][cid] = c;
28536                 c.row = rid;
28537                 c.col = cid;
28538                 if (c.rowspan < 2) {
28539                     return;
28540                 }
28541                 
28542                 for(var i = 1 ;i < c.rowspan; i++) {
28543                     if (typeof(ret[rid+i]) == 'undefined') {
28544                         ret[rid+i] = [];
28545                     }
28546                     ret[rid+i][cid] = c;
28547                 }
28548             });
28549         }, this);
28550         return ret;
28551     
28552     },
28553     
28554     normalizeRow: function(row)
28555     {
28556         var ret= [];
28557         row.forEach(function(c) {
28558             if (c.colspan < 2) {
28559                 ret.push(c);
28560                 return;
28561             }
28562             for(var i =0 ;i < c.colspan; i++) {
28563                 ret.push(c);
28564             }
28565         });
28566         return ret;
28567     
28568     },
28569     
28570     deleteColumn : function(sel)
28571     {
28572         if (!sel || sel.type != 'col') {
28573             return;
28574         }
28575         if (this.no_col < 2) {
28576             return;
28577         }
28578         
28579         this.rows.forEach(function(row) {
28580             var cols = this.normalizeRow(row);
28581             var col = cols[sel.col];
28582             if (col.colspan > 1) {
28583                 col.colspan --;
28584             } else {
28585                 row.remove(col);
28586             }
28587             
28588         }, this);
28589         this.no_col--;
28590         
28591     },
28592     removeColumn : function()
28593     {
28594         this.deleteColumn({
28595             type: 'col',
28596             col : this.no_col-1
28597         });
28598         this.updateElement();
28599     },
28600     
28601      
28602     addColumn : function()
28603     {
28604         
28605         this.rows.forEach(function(row) {
28606             row.push(this.emptyCell());
28607            
28608         }, this);
28609         this.updateElement();
28610     },
28611     
28612     deleteRow : function(sel)
28613     {
28614         if (!sel || sel.type != 'row') {
28615             return;
28616         }
28617         
28618         if (this.no_row < 2) {
28619             return;
28620         }
28621         
28622         var rows = this.normalizeRows();
28623         
28624         
28625         rows[sel.row].forEach(function(col) {
28626             if (col.rowspan > 1) {
28627                 col.rowspan--;
28628             } else {
28629                 col.remove = 1; // flage it as removed.
28630             }
28631             
28632         }, this);
28633         var newrows = [];
28634         this.rows.forEach(function(row) {
28635             newrow = [];
28636             row.forEach(function(c) {
28637                 if (typeof(c.remove) == 'undefined') {
28638                     newrow.push(c);
28639                 }
28640                 
28641             });
28642             if (newrow.length > 0) {
28643                 newrows.push(row);
28644             }
28645         });
28646         this.rows =  newrows;
28647         
28648         
28649         
28650         this.no_row--;
28651         this.updateElement();
28652         
28653     },
28654     removeRow : function()
28655     {
28656         this.deleteRow({
28657             type: 'row',
28658             row : this.no_row-1
28659         });
28660         
28661     },
28662     
28663      
28664     addRow : function()
28665     {
28666         
28667         var row = [];
28668         for (var i = 0; i < this.no_col; i++ ) {
28669             
28670             row.push(this.emptyCell());
28671            
28672         }
28673         this.rows.push(row);
28674         this.updateElement();
28675         
28676     },
28677      
28678     // the default cell object... at present...
28679     emptyCell : function() {
28680         return (new Roo.htmleditor.BlockTd({})).toObject();
28681         
28682      
28683     },
28684     
28685     removeNode : function()
28686     {
28687         return this.node;
28688     },
28689     
28690     
28691     
28692     resetWidths : function()
28693     {
28694         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28695             var nn = Roo.htmleditor.Block.factory(n);
28696             nn.width = '';
28697             nn.updateElement(n);
28698         });
28699     }
28700     
28701     
28702     
28703     
28704 })
28705
28706 /**
28707  *
28708  * editing a TD?
28709  *
28710  * since selections really work on the table cell, then editing really should work from there
28711  *
28712  * The original plan was to support merging etc... - but that may not be needed yet..
28713  *
28714  * So this simple version will support:
28715  *   add/remove cols
28716  *   adjust the width +/-
28717  *   reset the width...
28718  *   
28719  *
28720  */
28721
28722
28723  
28724
28725 /**
28726  * @class Roo.htmleditor.BlockTable
28727  * Block that manages a table
28728  * 
28729  * @constructor
28730  * Create a new Filter.
28731  * @param {Object} config Configuration options
28732  */
28733
28734 Roo.htmleditor.BlockTd = function(cfg)
28735 {
28736     if (cfg.node) {
28737         this.readElement(cfg.node);
28738         this.updateElement(cfg.node);
28739     }
28740     Roo.apply(this, cfg);
28741      
28742     
28743     
28744 }
28745 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28746  
28747     node : false,
28748     
28749     width: '',
28750     textAlign : 'left',
28751     valign : 'top',
28752     
28753     colspan : 1,
28754     rowspan : 1,
28755     
28756     
28757     // used by context menu
28758     friendly_name : 'Table Cell',
28759     deleteTitle : false, // use our customer delete
28760     
28761     // context menu is drawn once..
28762     
28763     contextMenu : function(toolbar)
28764     {
28765         
28766         var cell = function() {
28767             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28768         };
28769         
28770         var table = function() {
28771             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28772         };
28773         
28774         var lr = false;
28775         var saveSel = function()
28776         {
28777             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28778         }
28779         var restoreSel = function()
28780         {
28781             if (lr) {
28782                 (function() {
28783                     toolbar.editorcore.focus();
28784                     var cr = toolbar.editorcore.getSelection();
28785                     cr.removeAllRanges();
28786                     cr.addRange(lr);
28787                     toolbar.editorcore.onEditorEvent();
28788                 }).defer(10, this);
28789                 
28790                 
28791             }
28792         }
28793         
28794         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28795         
28796         var syncValue = toolbar.editorcore.syncValue;
28797         
28798         var fields = {};
28799         
28800         return [
28801             {
28802                 xtype : 'Button',
28803                 text : 'Edit Table',
28804                 listeners : {
28805                     click : function() {
28806                         var t = toolbar.tb.selectedNode.closest('table');
28807                         toolbar.editorcore.selectNode(t);
28808                         toolbar.editorcore.onEditorEvent();                        
28809                     }
28810                 }
28811                 
28812             },
28813               
28814            
28815              
28816             {
28817                 xtype : 'TextItem',
28818                 text : "Column Width: ",
28819                  xns : rooui.Toolbar 
28820                
28821             },
28822             {
28823                 xtype : 'Button',
28824                 text: '-',
28825                 listeners : {
28826                     click : function (_self, e)
28827                     {
28828                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28829                         cell().shrinkColumn();
28830                         syncValue();
28831                          toolbar.editorcore.onEditorEvent();
28832                     }
28833                 },
28834                 xns : rooui.Toolbar
28835             },
28836             {
28837                 xtype : 'Button',
28838                 text: '+',
28839                 listeners : {
28840                     click : function (_self, e)
28841                     {
28842                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28843                         cell().growColumn();
28844                         syncValue();
28845                         toolbar.editorcore.onEditorEvent();
28846                     }
28847                 },
28848                 xns : rooui.Toolbar
28849             },
28850             
28851             {
28852                 xtype : 'TextItem',
28853                 text : "Vertical Align: ",
28854                 xns : rooui.Toolbar  //Boostrap?
28855             },
28856             {
28857                 xtype : 'ComboBox',
28858                 allowBlank : false,
28859                 displayField : 'val',
28860                 editable : true,
28861                 listWidth : 100,
28862                 triggerAction : 'all',
28863                 typeAhead : true,
28864                 valueField : 'val',
28865                 width : 100,
28866                 name : 'valign',
28867                 listeners : {
28868                     select : function (combo, r, index)
28869                     {
28870                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28871                         var b = cell();
28872                         b.valign = r.get('val');
28873                         b.updateElement();
28874                         syncValue();
28875                         toolbar.editorcore.onEditorEvent();
28876                     }
28877                 },
28878                 xns : rooui.form,
28879                 store : {
28880                     xtype : 'SimpleStore',
28881                     data : [
28882                         ['top'],
28883                         ['middle'],
28884                         ['bottom'] // there are afew more... 
28885                     ],
28886                     fields : [ 'val'],
28887                     xns : Roo.data
28888                 }
28889             },
28890             
28891             {
28892                 xtype : 'TextItem',
28893                 text : "Merge Cells: ",
28894                  xns : rooui.Toolbar 
28895                
28896             },
28897             
28898             
28899             {
28900                 xtype : 'Button',
28901                 text: 'Right',
28902                 listeners : {
28903                     click : function (_self, e)
28904                     {
28905                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28906                         cell().mergeRight();
28907                         //block().growColumn();
28908                         syncValue();
28909                         toolbar.editorcore.onEditorEvent();
28910                     }
28911                 },
28912                 xns : rooui.Toolbar
28913             },
28914              
28915             {
28916                 xtype : 'Button',
28917                 text: 'Below',
28918                 listeners : {
28919                     click : function (_self, e)
28920                     {
28921                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28922                         cell().mergeBelow();
28923                         //block().growColumn();
28924                         syncValue();
28925                         toolbar.editorcore.onEditorEvent();
28926                     }
28927                 },
28928                 xns : rooui.Toolbar
28929             },
28930             {
28931                 xtype : 'TextItem',
28932                 text : "| ",
28933                  xns : rooui.Toolbar 
28934                
28935             },
28936             
28937             {
28938                 xtype : 'Button',
28939                 text: 'Split',
28940                 listeners : {
28941                     click : function (_self, e)
28942                     {
28943                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28944                         cell().split();
28945                         syncValue();
28946                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28947                         toolbar.editorcore.onEditorEvent();
28948                                              
28949                     }
28950                 },
28951                 xns : rooui.Toolbar
28952             },
28953             {
28954                 xtype : 'Fill',
28955                 xns : rooui.Toolbar 
28956                
28957             },
28958         
28959           
28960             {
28961                 xtype : 'Button',
28962                 text: 'Delete',
28963                  
28964                 xns : rooui.Toolbar,
28965                 menu : {
28966                     xtype : 'Menu',
28967                     xns : rooui.menu,
28968                     items : [
28969                         {
28970                             xtype : 'Item',
28971                             html: 'Column',
28972                             listeners : {
28973                                 click : function (_self, e)
28974                                 {
28975                                     var t = table();
28976                                     
28977                                     cell().deleteColumn();
28978                                     syncValue();
28979                                     toolbar.editorcore.selectNode(t.node);
28980                                     toolbar.editorcore.onEditorEvent();   
28981                                 }
28982                             },
28983                             xns : rooui.menu
28984                         },
28985                         {
28986                             xtype : 'Item',
28987                             html: 'Row',
28988                             listeners : {
28989                                 click : function (_self, e)
28990                                 {
28991                                     var t = table();
28992                                     cell().deleteRow();
28993                                     syncValue();
28994                                     
28995                                     toolbar.editorcore.selectNode(t.node);
28996                                     toolbar.editorcore.onEditorEvent();   
28997                                                          
28998                                 }
28999                             },
29000                             xns : rooui.menu
29001                         },
29002                        {
29003                             xtype : 'Separator',
29004                             xns : rooui.menu
29005                         },
29006                         {
29007                             xtype : 'Item',
29008                             html: 'Table',
29009                             listeners : {
29010                                 click : function (_self, e)
29011                                 {
29012                                     var t = table();
29013                                     var nn = t.node.nextSibling || t.node.previousSibling;
29014                                     t.node.parentNode.removeChild(t.node);
29015                                     if (nn) { 
29016                                         toolbar.editorcore.selectNode(nn, true);
29017                                     }
29018                                     toolbar.editorcore.onEditorEvent();   
29019                                                          
29020                                 }
29021                             },
29022                             xns : rooui.menu
29023                         }
29024                     ]
29025                 }
29026             }
29027             
29028             // align... << fixme
29029             
29030         ];
29031         
29032     },
29033     
29034     
29035   /**
29036      * create a DomHelper friendly object - for use with
29037      * Roo.DomHelper.markup / overwrite / etc..
29038      * ?? should it be called with option to hide all editing features?
29039      */
29040  /**
29041      * create a DomHelper friendly object - for use with
29042      * Roo.DomHelper.markup / overwrite / etc..
29043      * ?? should it be called with option to hide all editing features?
29044      */
29045     toObject : function()
29046     {
29047         var ret = {
29048             tag : 'td',
29049             contenteditable : 'true', // this stops cell selection from picking the table.
29050             'data-block' : 'Td',
29051             valign : this.valign,
29052             style : {  
29053                 'text-align' :  this.textAlign,
29054                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29055                 'border-collapse' : 'collapse',
29056                 padding : '6px', // 8 for desktop / 4 for mobile
29057                 'vertical-align': this.valign
29058             },
29059             html : this.html
29060         };
29061         if (this.width != '') {
29062             ret.width = this.width;
29063             ret.style.width = this.width;
29064         }
29065         
29066         
29067         if (this.colspan > 1) {
29068             ret.colspan = this.colspan ;
29069         } 
29070         if (this.rowspan > 1) {
29071             ret.rowspan = this.rowspan ;
29072         }
29073         
29074            
29075         
29076         return ret;
29077          
29078     },
29079     
29080     readElement : function(node)
29081     {
29082         node  = node ? node : this.node ;
29083         this.width = node.style.width;
29084         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29085         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29086         this.html = node.innerHTML;
29087         if (node.style.textAlign != '') {
29088             this.textAlign = node.style.textAlign;
29089         }
29090         
29091         
29092     },
29093      
29094     // the default cell object... at present...
29095     emptyCell : function() {
29096         return {
29097             colspan :  1,
29098             rowspan :  1,
29099             textAlign : 'left',
29100             html : "&nbsp;" // is this going to be editable now?
29101         };
29102      
29103     },
29104     
29105     removeNode : function()
29106     {
29107         return this.node.closest('table');
29108          
29109     },
29110     
29111     cellData : false,
29112     
29113     colWidths : false,
29114     
29115     toTableArray  : function()
29116     {
29117         var ret = [];
29118         var tab = this.node.closest('tr').closest('table');
29119         Array.from(tab.rows).forEach(function(r, ri){
29120             ret[ri] = [];
29121         });
29122         var rn = 0;
29123         this.colWidths = [];
29124         var all_auto = true;
29125         Array.from(tab.rows).forEach(function(r, ri){
29126             
29127             var cn = 0;
29128             Array.from(r.cells).forEach(function(ce, ci){
29129                 var c =  {
29130                     cell : ce,
29131                     row : rn,
29132                     col: cn,
29133                     colspan : ce.colSpan,
29134                     rowspan : ce.rowSpan
29135                 };
29136                 if (ce.isEqualNode(this.node)) {
29137                     this.cellData = c;
29138                 }
29139                 // if we have been filled up by a row?
29140                 if (typeof(ret[rn][cn]) != 'undefined') {
29141                     while(typeof(ret[rn][cn]) != 'undefined') {
29142                         cn++;
29143                     }
29144                     c.col = cn;
29145                 }
29146                 
29147                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29148                     this.colWidths[cn] =   ce.style.width;
29149                     if (this.colWidths[cn] != '') {
29150                         all_auto = false;
29151                     }
29152                 }
29153                 
29154                 
29155                 if (c.colspan < 2 && c.rowspan < 2 ) {
29156                     ret[rn][cn] = c;
29157                     cn++;
29158                     return;
29159                 }
29160                 for(var j = 0; j < c.rowspan; j++) {
29161                     if (typeof(ret[rn+j]) == 'undefined') {
29162                         continue; // we have a problem..
29163                     }
29164                     ret[rn+j][cn] = c;
29165                     for(var i = 0; i < c.colspan; i++) {
29166                         ret[rn+j][cn+i] = c;
29167                     }
29168                 }
29169                 
29170                 cn += c.colspan;
29171             }, this);
29172             rn++;
29173         }, this);
29174         
29175         // initalize widths.?
29176         // either all widths or no widths..
29177         if (all_auto) {
29178             this.colWidths[0] = false; // no widths flag.
29179         }
29180         
29181         
29182         return ret;
29183         
29184     },
29185     
29186     
29187     
29188     
29189     mergeRight: function()
29190     {
29191          
29192         // get the contents of the next cell along..
29193         var tr = this.node.closest('tr');
29194         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29195         if (i >= tr.childNodes.length - 1) {
29196             return; // no cells on right to merge with.
29197         }
29198         var table = this.toTableArray();
29199         
29200         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29201             return; // nothing right?
29202         }
29203         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29204         // right cell - must be same rowspan and on the same row.
29205         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29206             return; // right hand side is not same rowspan.
29207         }
29208         
29209         
29210         
29211         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29212         tr.removeChild(rc.cell);
29213         this.colspan += rc.colspan;
29214         this.node.setAttribute('colspan', this.colspan);
29215
29216         var table = this.toTableArray();
29217         this.normalizeWidths(table);
29218         this.updateWidths(table);
29219     },
29220     
29221     
29222     mergeBelow : function()
29223     {
29224         var table = this.toTableArray();
29225         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
29226             return; // no row below
29227         }
29228         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
29229             return; // nothing right?
29230         }
29231         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
29232         
29233         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
29234             return; // right hand side is not same rowspan.
29235         }
29236         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
29237         rc.cell.parentNode.removeChild(rc.cell);
29238         this.rowspan += rc.rowspan;
29239         this.node.setAttribute('rowspan', this.rowspan);
29240     },
29241     
29242     split: function()
29243     {
29244         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
29245             return;
29246         }
29247         var table = this.toTableArray();
29248         var cd = this.cellData;
29249         this.rowspan = 1;
29250         this.colspan = 1;
29251         
29252         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
29253              
29254             
29255             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
29256                 if (r == cd.row && c == cd.col) {
29257                     this.node.removeAttribute('rowspan');
29258                     this.node.removeAttribute('colspan');
29259                 }
29260                  
29261                 var ntd = this.node.cloneNode(); // which col/row should be 0..
29262                 ntd.removeAttribute('id'); 
29263                 ntd.style.width  = this.colWidths[c];
29264                 ntd.innerHTML = '';
29265                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
29266             }
29267             
29268         }
29269         this.redrawAllCells(table);
29270         
29271     },
29272     
29273     
29274     
29275     redrawAllCells: function(table)
29276     {
29277         
29278          
29279         var tab = this.node.closest('tr').closest('table');
29280         var ctr = tab.rows[0].parentNode;
29281         Array.from(tab.rows).forEach(function(r, ri){
29282             
29283             Array.from(r.cells).forEach(function(ce, ci){
29284                 ce.parentNode.removeChild(ce);
29285             });
29286             r.parentNode.removeChild(r);
29287         });
29288         for(var r = 0 ; r < table.length; r++) {
29289             var re = tab.rows[r];
29290             
29291             var re = tab.ownerDocument.createElement('tr');
29292             ctr.appendChild(re);
29293             for(var c = 0 ; c < table[r].length; c++) {
29294                 if (table[r][c].cell === false) {
29295                     continue;
29296                 }
29297                 
29298                 re.appendChild(table[r][c].cell);
29299                  
29300                 table[r][c].cell = false;
29301             }
29302         }
29303         
29304     },
29305     updateWidths : function(table)
29306     {
29307         for(var r = 0 ; r < table.length; r++) {
29308            
29309             for(var c = 0 ; c < table[r].length; c++) {
29310                 if (table[r][c].cell === false) {
29311                     continue;
29312                 }
29313                 
29314                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
29315                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
29316                     el.width = Math.floor(this.colWidths[c])  +'%';
29317                     el.updateElement(el.node);
29318                 }
29319                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
29320                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
29321                     var width = 0;
29322                     for(var i = 0; i < table[r][c].colspan; i ++) {
29323                         width += Math.floor(this.colWidths[c + i]);
29324                     }
29325                     el.width = width  +'%';
29326                     el.updateElement(el.node);
29327                 }
29328                 table[r][c].cell = false; // done
29329             }
29330         }
29331     },
29332     normalizeWidths : function(table)
29333     {
29334         if (this.colWidths[0] === false) {
29335             var nw = 100.0 / this.colWidths.length;
29336             this.colWidths.forEach(function(w,i) {
29337                 this.colWidths[i] = nw;
29338             },this);
29339             return;
29340         }
29341     
29342         var t = 0, missing = [];
29343         
29344         this.colWidths.forEach(function(w,i) {
29345             //if you mix % and
29346             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
29347             var add =  this.colWidths[i];
29348             if (add > 0) {
29349                 t+=add;
29350                 return;
29351             }
29352             missing.push(i);
29353             
29354             
29355         },this);
29356         var nc = this.colWidths.length;
29357         if (missing.length) {
29358             var mult = (nc - missing.length) / (1.0 * nc);
29359             var t = mult * t;
29360             var ew = (100 -t) / (1.0 * missing.length);
29361             this.colWidths.forEach(function(w,i) {
29362                 if (w > 0) {
29363                     this.colWidths[i] = w * mult;
29364                     return;
29365                 }
29366                 
29367                 this.colWidths[i] = ew;
29368             }, this);
29369             // have to make up numbers..
29370              
29371         }
29372         // now we should have all the widths..
29373         
29374     
29375     },
29376     
29377     shrinkColumn : function()
29378     {
29379         var table = this.toTableArray();
29380         this.normalizeWidths(table);
29381         var col = this.cellData.col;
29382         var nw = this.colWidths[col] * 0.8;
29383         if (nw < 5) {
29384             return;
29385         }
29386         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
29387         this.colWidths.forEach(function(w,i) {
29388             if (i == col) {
29389                  this.colWidths[i] = nw;
29390                 return;
29391             }
29392             this.colWidths[i] += otherAdd
29393         }, this);
29394         this.updateWidths(table);
29395          
29396     },
29397     growColumn : function()
29398     {
29399         var table = this.toTableArray();
29400         this.normalizeWidths(table);
29401         var col = this.cellData.col;
29402         var nw = this.colWidths[col] * 1.2;
29403         if (nw > 90) {
29404             return;
29405         }
29406         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
29407         this.colWidths.forEach(function(w,i) {
29408             if (i == col) {
29409                 this.colWidths[i] = nw;
29410                 return;
29411             }
29412             this.colWidths[i] -= otherSub
29413         }, this);
29414         this.updateWidths(table);
29415          
29416     },
29417     deleteRow : function()
29418     {
29419         // delete this rows 'tr'
29420         // if any of the cells in this row have a rowspan > 1 && row!= this row..
29421         // then reduce the rowspan.
29422         var table = this.toTableArray();
29423         // this.cellData.row;
29424         for (var i =0;i< table[this.cellData.row].length ; i++) {
29425             var c = table[this.cellData.row][i];
29426             if (c.row != this.cellData.row) {
29427                 
29428                 c.rowspan--;
29429                 c.cell.setAttribute('rowspan', c.rowspan);
29430                 continue;
29431             }
29432             if (c.rowspan > 1) {
29433                 c.rowspan--;
29434                 c.cell.setAttribute('rowspan', c.rowspan);
29435             }
29436         }
29437         table.splice(this.cellData.row,1);
29438         this.redrawAllCells(table);
29439         
29440     },
29441     deleteColumn : function()
29442     {
29443         var table = this.toTableArray();
29444         
29445         for (var i =0;i< table.length ; i++) {
29446             var c = table[i][this.cellData.col];
29447             if (c.col != this.cellData.col) {
29448                 table[i][this.cellData.col].colspan--;
29449             } else if (c.colspan > 1) {
29450                 c.colspan--;
29451                 c.cell.setAttribute('colspan', c.colspan);
29452             }
29453             table[i].splice(this.cellData.col,1);
29454         }
29455         
29456         this.redrawAllCells(table);
29457     }
29458     
29459     
29460     
29461     
29462 })
29463
29464 //<script type="text/javascript">
29465
29466 /*
29467  * Based  Ext JS Library 1.1.1
29468  * Copyright(c) 2006-2007, Ext JS, LLC.
29469  * LGPL
29470  *
29471  */
29472  
29473 /**
29474  * @class Roo.HtmlEditorCore
29475  * @extends Roo.Component
29476  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
29477  *
29478  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
29479  */
29480
29481 Roo.HtmlEditorCore = function(config){
29482     
29483     
29484     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
29485     
29486     
29487     this.addEvents({
29488         /**
29489          * @event initialize
29490          * Fires when the editor is fully initialized (including the iframe)
29491          * @param {Roo.HtmlEditorCore} this
29492          */
29493         initialize: true,
29494         /**
29495          * @event activate
29496          * Fires when the editor is first receives the focus. Any insertion must wait
29497          * until after this event.
29498          * @param {Roo.HtmlEditorCore} this
29499          */
29500         activate: true,
29501          /**
29502          * @event beforesync
29503          * Fires before the textarea is updated with content from the editor iframe. Return false
29504          * to cancel the sync.
29505          * @param {Roo.HtmlEditorCore} this
29506          * @param {String} html
29507          */
29508         beforesync: true,
29509          /**
29510          * @event beforepush
29511          * Fires before the iframe editor is updated with content from the textarea. Return false
29512          * to cancel the push.
29513          * @param {Roo.HtmlEditorCore} this
29514          * @param {String} html
29515          */
29516         beforepush: true,
29517          /**
29518          * @event sync
29519          * Fires when the textarea is updated with content from the editor iframe.
29520          * @param {Roo.HtmlEditorCore} this
29521          * @param {String} html
29522          */
29523         sync: true,
29524          /**
29525          * @event push
29526          * Fires when the iframe editor is updated with content from the textarea.
29527          * @param {Roo.HtmlEditorCore} this
29528          * @param {String} html
29529          */
29530         push: true,
29531         
29532         /**
29533          * @event editorevent
29534          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29535          * @param {Roo.HtmlEditorCore} this
29536          */
29537         editorevent: true 
29538          
29539         
29540     });
29541     
29542     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29543     
29544     // defaults : white / black...
29545     this.applyBlacklists();
29546     
29547     
29548     
29549 };
29550
29551
29552 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
29553
29554
29555      /**
29556      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
29557      */
29558     
29559     owner : false,
29560     
29561      /**
29562      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
29563      *                        Roo.resizable.
29564      */
29565     resizable : false,
29566      /**
29567      * @cfg {Number} height (in pixels)
29568      */   
29569     height: 300,
29570    /**
29571      * @cfg {Number} width (in pixels)
29572      */   
29573     width: 500,
29574      /**
29575      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29576      *         if you are doing an email editor, this probably needs disabling, it's designed
29577      */
29578     autoClean: true,
29579     
29580     /**
29581      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29582      */
29583     enableBlocks : true,
29584     /**
29585      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29586      * 
29587      */
29588     stylesheets: false,
29589      /**
29590      * @cfg {String} language default en - language of text (usefull for rtl languages)
29591      * 
29592      */
29593     language: 'en',
29594     
29595     /**
29596      * @cfg {boolean} allowComments - default false - allow comments in HTML source
29597      *          - by default they are stripped - if you are editing email you may need this.
29598      */
29599     allowComments: false,
29600     // id of frame..
29601     frameId: false,
29602     
29603     // private properties
29604     validationEvent : false,
29605     deferHeight: true,
29606     initialized : false,
29607     activated : false,
29608     sourceEditMode : false,
29609     onFocus : Roo.emptyFn,
29610     iframePad:3,
29611     hideMode:'offsets',
29612     
29613     clearUp: true,
29614     
29615     // blacklist + whitelisted elements..
29616     black: false,
29617     white: false,
29618      
29619     bodyCls : '',
29620
29621     
29622     undoManager : false,
29623     /**
29624      * Protected method that will not generally be called directly. It
29625      * is called when the editor initializes the iframe with HTML contents. Override this method if you
29626      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29627      */
29628     getDocMarkup : function(){
29629         // body styles..
29630         var st = '';
29631         
29632         // inherit styels from page...?? 
29633         if (this.stylesheets === false) {
29634             
29635             Roo.get(document.head).select('style').each(function(node) {
29636                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29637             });
29638             
29639             Roo.get(document.head).select('link').each(function(node) { 
29640                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29641             });
29642             
29643         } else if (!this.stylesheets.length) {
29644                 // simple..
29645                 st = '<style type="text/css">' +
29646                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29647                    '</style>';
29648         } else {
29649             for (var i in this.stylesheets) {
29650                 if (typeof(this.stylesheets[i]) != 'string') {
29651                     continue;
29652                 }
29653                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29654             }
29655             
29656         }
29657         
29658         st +=  '<style type="text/css">' +
29659             'IMG { cursor: pointer } ' +
29660         '</style>';
29661         
29662         st += '<meta name="google" content="notranslate">';
29663         
29664         var cls = 'notranslate roo-htmleditor-body';
29665         
29666         if(this.bodyCls.length){
29667             cls += ' ' + this.bodyCls;
29668         }
29669         
29670         return '<html  class="notranslate" translate="no"><head>' + st  +
29671             //<style type="text/css">' +
29672             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29673             //'</style>' +
29674             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
29675     },
29676
29677     // private
29678     onRender : function(ct, position)
29679     {
29680         var _t = this;
29681         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29682         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29683         
29684         
29685         this.el.dom.style.border = '0 none';
29686         this.el.dom.setAttribute('tabIndex', -1);
29687         this.el.addClass('x-hidden hide');
29688         
29689         
29690         
29691         if(Roo.isIE){ // fix IE 1px bogus margin
29692             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29693         }
29694        
29695         
29696         this.frameId = Roo.id();
29697         
29698          
29699         
29700         var iframe = this.owner.wrap.createChild({
29701             tag: 'iframe',
29702             cls: 'form-control', // bootstrap..
29703             id: this.frameId,
29704             name: this.frameId,
29705             frameBorder : 'no',
29706             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29707         }, this.el
29708         );
29709         
29710         
29711         this.iframe = iframe.dom;
29712
29713         this.assignDocWin();
29714         
29715         this.doc.designMode = 'on';
29716        
29717         this.doc.open();
29718         this.doc.write(this.getDocMarkup());
29719         this.doc.close();
29720
29721         
29722         var task = { // must defer to wait for browser to be ready
29723             run : function(){
29724                 //console.log("run task?" + this.doc.readyState);
29725                 this.assignDocWin();
29726                 if(this.doc.body || this.doc.readyState == 'complete'){
29727                     try {
29728                         this.doc.designMode="on";
29729                         
29730                     } catch (e) {
29731                         return;
29732                     }
29733                     Roo.TaskMgr.stop(task);
29734                     this.initEditor.defer(10, this);
29735                 }
29736             },
29737             interval : 10,
29738             duration: 10000,
29739             scope: this
29740         };
29741         Roo.TaskMgr.start(task);
29742
29743     },
29744
29745     // private
29746     onResize : function(w, h)
29747     {
29748          Roo.log('resize: ' +w + ',' + h );
29749         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29750         if(!this.iframe){
29751             return;
29752         }
29753         if(typeof w == 'number'){
29754             
29755             this.iframe.style.width = w + 'px';
29756         }
29757         if(typeof h == 'number'){
29758             
29759             this.iframe.style.height = h + 'px';
29760             if(this.doc){
29761                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29762             }
29763         }
29764         
29765     },
29766
29767     /**
29768      * Toggles the editor between standard and source edit mode.
29769      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29770      */
29771     toggleSourceEdit : function(sourceEditMode){
29772         
29773         this.sourceEditMode = sourceEditMode === true;
29774         
29775         if(this.sourceEditMode){
29776  
29777             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29778             
29779         }else{
29780             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29781             //this.iframe.className = '';
29782             this.deferFocus();
29783         }
29784         //this.setSize(this.owner.wrap.getSize());
29785         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29786     },
29787
29788     
29789   
29790
29791     /**
29792      * Protected method that will not generally be called directly. If you need/want
29793      * custom HTML cleanup, this is the method you should override.
29794      * @param {String} html The HTML to be cleaned
29795      * return {String} The cleaned HTML
29796      */
29797     cleanHtml : function(html)
29798     {
29799         html = String(html);
29800         if(html.length > 5){
29801             if(Roo.isSafari){ // strip safari nonsense
29802                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29803             }
29804         }
29805         if(html == '&nbsp;'){
29806             html = '';
29807         }
29808         return html;
29809     },
29810
29811     /**
29812      * HTML Editor -> Textarea
29813      * Protected method that will not generally be called directly. Syncs the contents
29814      * of the editor iframe with the textarea.
29815      */
29816     syncValue : function()
29817     {
29818         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29819         if(this.initialized){
29820             
29821             if (this.undoManager) {
29822                 this.undoManager.addEvent();
29823             }
29824
29825             
29826             var bd = (this.doc.body || this.doc.documentElement);
29827            
29828             
29829             var sel = this.win.getSelection();
29830             
29831             var div = document.createElement('div');
29832             div.innerHTML = bd.innerHTML;
29833             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29834             if (gtx.length > 0) {
29835                 var rm = gtx.item(0).parentNode;
29836                 rm.parentNode.removeChild(rm);
29837             }
29838             
29839            
29840             if (this.enableBlocks) {
29841                 new Roo.htmleditor.FilterBlock({ node : div });
29842             }
29843             
29844             var html = div.innerHTML;
29845             
29846             //?? tidy?
29847             if (this.autoClean) {
29848                 
29849                 new Roo.htmleditor.FilterAttributes({
29850                     node : div,
29851                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29852                     attrib_clean : ['href', 'src' ] 
29853                 });
29854                 
29855                 var tidy = new Roo.htmleditor.TidySerializer({
29856                     inner:  true
29857                 });
29858                 html  = tidy.serialize(div);
29859                 
29860             }
29861             
29862             
29863             if(Roo.isSafari){
29864                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29865                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29866                 if(m && m[1]){
29867                     html = '<div style="'+m[0]+'">' + html + '</div>';
29868                 }
29869             }
29870             html = this.cleanHtml(html);
29871             // fix up the special chars.. normaly like back quotes in word...
29872             // however we do not want to do this with chinese..
29873             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29874                 
29875                 var cc = match.charCodeAt();
29876
29877                 // Get the character value, handling surrogate pairs
29878                 if (match.length == 2) {
29879                     // It's a surrogate pair, calculate the Unicode code point
29880                     var high = match.charCodeAt(0) - 0xD800;
29881                     var low  = match.charCodeAt(1) - 0xDC00;
29882                     cc = (high * 0x400) + low + 0x10000;
29883                 }  else if (
29884                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29885                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29886                     (cc >= 0xf900 && cc < 0xfb00 )
29887                 ) {
29888                         return match;
29889                 }  
29890          
29891                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29892                 return "&#" + cc + ";";
29893                 
29894                 
29895             });
29896             
29897             
29898              
29899             if(this.owner.fireEvent('beforesync', this, html) !== false){
29900                 this.el.dom.value = html;
29901                 this.owner.fireEvent('sync', this, html);
29902             }
29903         }
29904     },
29905
29906     /**
29907      * TEXTAREA -> EDITABLE
29908      * Protected method that will not generally be called directly. Pushes the value of the textarea
29909      * into the iframe editor.
29910      */
29911     pushValue : function()
29912     {
29913         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29914         if(this.initialized){
29915             var v = this.el.dom.value.trim();
29916             
29917             
29918             if(this.owner.fireEvent('beforepush', this, v) !== false){
29919                 var d = (this.doc.body || this.doc.documentElement);
29920                 d.innerHTML = v;
29921                  
29922                 this.el.dom.value = d.innerHTML;
29923                 this.owner.fireEvent('push', this, v);
29924             }
29925             if (this.autoClean) {
29926                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29927                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29928             }
29929             if (this.enableBlocks) {
29930                 Roo.htmleditor.Block.initAll(this.doc.body);
29931             }
29932             
29933             this.updateLanguage();
29934             
29935             var lc = this.doc.body.lastChild;
29936             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29937                 // add an extra line at the end.
29938                 this.doc.body.appendChild(this.doc.createElement('br'));
29939             }
29940             
29941             
29942         }
29943     },
29944
29945     // private
29946     deferFocus : function(){
29947         this.focus.defer(10, this);
29948     },
29949
29950     // doc'ed in Field
29951     focus : function(){
29952         if(this.win && !this.sourceEditMode){
29953             this.win.focus();
29954         }else{
29955             this.el.focus();
29956         }
29957     },
29958     
29959     assignDocWin: function()
29960     {
29961         var iframe = this.iframe;
29962         
29963          if(Roo.isIE){
29964             this.doc = iframe.contentWindow.document;
29965             this.win = iframe.contentWindow;
29966         } else {
29967 //            if (!Roo.get(this.frameId)) {
29968 //                return;
29969 //            }
29970 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29971 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29972             
29973             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29974                 return;
29975             }
29976             
29977             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29978             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29979         }
29980     },
29981     
29982     // private
29983     initEditor : function(){
29984         //console.log("INIT EDITOR");
29985         this.assignDocWin();
29986         
29987         
29988         
29989         this.doc.designMode="on";
29990         this.doc.open();
29991         this.doc.write(this.getDocMarkup());
29992         this.doc.close();
29993         
29994         var dbody = (this.doc.body || this.doc.documentElement);
29995         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29996         // this copies styles from the containing element into thsi one..
29997         // not sure why we need all of this..
29998         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29999         
30000         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30001         //ss['background-attachment'] = 'fixed'; // w3c
30002         dbody.bgProperties = 'fixed'; // ie
30003         dbody.setAttribute("translate", "no");
30004         
30005         //Roo.DomHelper.applyStyles(dbody, ss);
30006         Roo.EventManager.on(this.doc, {
30007              
30008             'mouseup': this.onEditorEvent,
30009             'dblclick': this.onEditorEvent,
30010             'click': this.onEditorEvent,
30011             'keyup': this.onEditorEvent,
30012             
30013             buffer:100,
30014             scope: this
30015         });
30016         Roo.EventManager.on(this.doc, {
30017             'paste': this.onPasteEvent,
30018             scope : this
30019         });
30020         if(Roo.isGecko){
30021             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30022         }
30023         //??? needed???
30024         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30025             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30026         }
30027         this.initialized = true;
30028
30029         
30030         // initialize special key events - enter
30031         new Roo.htmleditor.KeyEnter({core : this});
30032         
30033          
30034         
30035         this.owner.fireEvent('initialize', this);
30036         this.pushValue();
30037     },
30038     // this is to prevent a href clicks resulting in a redirect?
30039    
30040     onPasteEvent : function(e,v)
30041     {
30042         // I think we better assume paste is going to be a dirty load of rubish from word..
30043         
30044         // even pasting into a 'email version' of this widget will have to clean up that mess.
30045         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30046         
30047         // check what type of paste - if it's an image, then handle it differently.
30048         if (cd.files && cd.files.length > 0) {
30049             // pasting images?
30050             var urlAPI = (window.createObjectURL && window) || 
30051                 (window.URL && URL.revokeObjectURL && URL) || 
30052                 (window.webkitURL && webkitURL);
30053     
30054             var url = urlAPI.createObjectURL( cd.files[0]);
30055             this.insertAtCursor('<img src=" + url + ">');
30056             return false;
30057         }
30058         if (cd.types.indexOf('text/html') < 0 ) {
30059             return false;
30060         }
30061         var images = [];
30062         var html = cd.getData('text/html'); // clipboard event
30063         if (cd.types.indexOf('text/rtf') > -1) {
30064             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30065             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30066         }
30067         //Roo.log(images);
30068         //Roo.log(imgs);
30069         // fixme..
30070         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30071                        .map(function(g) { return g.toDataURL(); })
30072                        .filter(function(g) { return g != 'about:blank'; });
30073         
30074         //Roo.log(html);
30075         html = this.cleanWordChars(html);
30076         
30077         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30078         
30079         
30080         var sn = this.getParentElement();
30081         // check if d contains a table, and prevent nesting??
30082         //Roo.log(d.getElementsByTagName('table'));
30083         //Roo.log(sn);
30084         //Roo.log(sn.closest('table'));
30085         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30086             e.preventDefault();
30087             this.insertAtCursor("You can not nest tables");
30088             //Roo.log("prevent?"); // fixme - 
30089             return false;
30090         }
30091         
30092         
30093         
30094         if (images.length > 0) {
30095             // replace all v:imagedata - with img.
30096             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30097             Roo.each(ar, function(node) {
30098                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30099                 node.parentNode.removeChild(node);
30100             });
30101             
30102             
30103             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30104                 img.setAttribute('src', images[i]);
30105             });
30106         }
30107         if (this.autoClean) {
30108             new Roo.htmleditor.FilterWord({ node : d });
30109             
30110             new Roo.htmleditor.FilterStyleToTag({ node : d });
30111             new Roo.htmleditor.FilterAttributes({
30112                 node : d,
30113                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30114                 attrib_clean : ['href', 'src' ] 
30115             });
30116             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30117             // should be fonts..
30118             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30119             new Roo.htmleditor.FilterParagraph({ node : d });
30120             new Roo.htmleditor.FilterSpan({ node : d });
30121             new Roo.htmleditor.FilterLongBr({ node : d });
30122             new Roo.htmleditor.FilterComment({ node : d });
30123             
30124             
30125         }
30126         if (this.enableBlocks) {
30127                 
30128             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30129                 if (img.closest('figure')) { // assume!! that it's aready
30130                     return;
30131                 }
30132                 var fig  = new Roo.htmleditor.BlockFigure({
30133                     image_src  : img.src
30134                 });
30135                 fig.updateElement(img); // replace it..
30136                 
30137             });
30138         }
30139         
30140         
30141         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30142         if (this.enableBlocks) {
30143             Roo.htmleditor.Block.initAll(this.doc.body);
30144         }
30145          
30146         
30147         e.preventDefault();
30148         return false;
30149         // default behaveiour should be our local cleanup paste? (optional?)
30150         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30151         //this.owner.fireEvent('paste', e, v);
30152     },
30153     // private
30154     onDestroy : function(){
30155         
30156         
30157         
30158         if(this.rendered){
30159             
30160             //for (var i =0; i < this.toolbars.length;i++) {
30161             //    // fixme - ask toolbars for heights?
30162             //    this.toolbars[i].onDestroy();
30163            // }
30164             
30165             //this.wrap.dom.innerHTML = '';
30166             //this.wrap.remove();
30167         }
30168     },
30169
30170     // private
30171     onFirstFocus : function(){
30172         
30173         this.assignDocWin();
30174         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30175         
30176         this.activated = true;
30177          
30178     
30179         if(Roo.isGecko){ // prevent silly gecko errors
30180             this.win.focus();
30181             var s = this.win.getSelection();
30182             if(!s.focusNode || s.focusNode.nodeType != 3){
30183                 var r = s.getRangeAt(0);
30184                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30185                 r.collapse(true);
30186                 this.deferFocus();
30187             }
30188             try{
30189                 this.execCmd('useCSS', true);
30190                 this.execCmd('styleWithCSS', false);
30191             }catch(e){}
30192         }
30193         this.owner.fireEvent('activate', this);
30194     },
30195
30196     // private
30197     adjustFont: function(btn){
30198         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30199         //if(Roo.isSafari){ // safari
30200         //    adjust *= 2;
30201        // }
30202         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
30203         if(Roo.isSafari){ // safari
30204             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
30205             v =  (v < 10) ? 10 : v;
30206             v =  (v > 48) ? 48 : v;
30207             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
30208             
30209         }
30210         
30211         
30212         v = Math.max(1, v+adjust);
30213         
30214         this.execCmd('FontSize', v  );
30215     },
30216
30217     onEditorEvent : function(e)
30218     {
30219          
30220         
30221         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
30222             return; // we do not handle this.. (undo manager does..)
30223         }
30224         // in theory this detects if the last element is not a br, then we try and do that.
30225         // its so clicking in space at bottom triggers adding a br and moving the cursor.
30226         if (e &&
30227             e.target.nodeName == 'BODY' &&
30228             e.type == "mouseup" &&
30229             this.doc.body.lastChild
30230            ) {
30231             var lc = this.doc.body.lastChild;
30232             // gtx-trans is google translate plugin adding crap.
30233             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
30234                 lc = lc.previousSibling;
30235             }
30236             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
30237             // if last element is <BR> - then dont do anything.
30238             
30239                 var ns = this.doc.createElement('br');
30240                 this.doc.body.appendChild(ns);
30241                 range = this.doc.createRange();
30242                 range.setStartAfter(ns);
30243                 range.collapse(true);
30244                 var sel = this.win.getSelection();
30245                 sel.removeAllRanges();
30246                 sel.addRange(range);
30247             }
30248         }
30249         
30250         
30251         
30252         this.fireEditorEvent(e);
30253       //  this.updateToolbar();
30254         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
30255     },
30256     
30257     fireEditorEvent: function(e)
30258     {
30259         this.owner.fireEvent('editorevent', this, e);
30260     },
30261
30262     insertTag : function(tg)
30263     {
30264         // could be a bit smarter... -> wrap the current selected tRoo..
30265         if (tg.toLowerCase() == 'span' ||
30266             tg.toLowerCase() == 'code' ||
30267             tg.toLowerCase() == 'sup' ||
30268             tg.toLowerCase() == 'sub' 
30269             ) {
30270             
30271             range = this.createRange(this.getSelection());
30272             var wrappingNode = this.doc.createElement(tg.toLowerCase());
30273             wrappingNode.appendChild(range.extractContents());
30274             range.insertNode(wrappingNode);
30275
30276             return;
30277             
30278             
30279             
30280         }
30281         this.execCmd("formatblock",   tg);
30282         this.undoManager.addEvent(); 
30283     },
30284     
30285     insertText : function(txt)
30286     {
30287         
30288         
30289         var range = this.createRange();
30290         range.deleteContents();
30291                //alert(Sender.getAttribute('label'));
30292                
30293         range.insertNode(this.doc.createTextNode(txt));
30294         this.undoManager.addEvent();
30295     } ,
30296     
30297      
30298
30299     /**
30300      * Executes a Midas editor command on the editor document and performs necessary focus and
30301      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
30302      * @param {String} cmd The Midas command
30303      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
30304      */
30305     relayCmd : function(cmd, value)
30306     {
30307         
30308         switch (cmd) {
30309             case 'justifyleft':
30310             case 'justifyright':
30311             case 'justifycenter':
30312                 // if we are in a cell, then we will adjust the
30313                 var n = this.getParentElement();
30314                 var td = n.closest('td');
30315                 if (td) {
30316                     var bl = Roo.htmleditor.Block.factory(td);
30317                     bl.textAlign = cmd.replace('justify','');
30318                     bl.updateElement();
30319                     this.owner.fireEvent('editorevent', this);
30320                     return;
30321                 }
30322                 this.execCmd('styleWithCSS', true); // 
30323                 break;
30324             case 'bold':
30325             case 'italic':
30326                 // if there is no selection, then we insert, and set the curson inside it..
30327                 this.execCmd('styleWithCSS', false); 
30328                 break;
30329                 
30330         
30331             default:
30332                 break;
30333         }
30334         
30335         
30336         this.win.focus();
30337         this.execCmd(cmd, value);
30338         this.owner.fireEvent('editorevent', this);
30339         //this.updateToolbar();
30340         this.owner.deferFocus();
30341     },
30342
30343     /**
30344      * Executes a Midas editor command directly on the editor document.
30345      * For visual commands, you should use {@link #relayCmd} instead.
30346      * <b>This should only be called after the editor is initialized.</b>
30347      * @param {String} cmd The Midas command
30348      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
30349      */
30350     execCmd : function(cmd, value){
30351         this.doc.execCommand(cmd, false, value === undefined ? null : value);
30352         this.syncValue();
30353     },
30354  
30355  
30356    
30357     /**
30358      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
30359      * to insert tRoo.
30360      * @param {String} text | dom node.. 
30361      */
30362     insertAtCursor : function(text)
30363     {
30364         
30365         if(!this.activated){
30366             return;
30367         }
30368          
30369         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
30370             this.win.focus();
30371             
30372             
30373             // from jquery ui (MIT licenced)
30374             var range, node;
30375             var win = this.win;
30376             
30377             if (win.getSelection && win.getSelection().getRangeAt) {
30378                 
30379                 // delete the existing?
30380                 
30381                 this.createRange(this.getSelection()).deleteContents();
30382                 range = win.getSelection().getRangeAt(0);
30383                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
30384                 range.insertNode(node);
30385                 range = range.cloneRange();
30386                 range.collapse(false);
30387                  
30388                 win.getSelection().removeAllRanges();
30389                 win.getSelection().addRange(range);
30390                 
30391                 
30392                 
30393             } else if (win.document.selection && win.document.selection.createRange) {
30394                 // no firefox support
30395                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
30396                 win.document.selection.createRange().pasteHTML(txt);
30397             
30398             } else {
30399                 // no firefox support
30400                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
30401                 this.execCmd('InsertHTML', txt);
30402             } 
30403             this.syncValue();
30404             
30405             this.deferFocus();
30406         }
30407     },
30408  // private
30409     mozKeyPress : function(e){
30410         if(e.ctrlKey){
30411             var c = e.getCharCode(), cmd;
30412           
30413             if(c > 0){
30414                 c = String.fromCharCode(c).toLowerCase();
30415                 switch(c){
30416                     case 'b':
30417                         cmd = 'bold';
30418                         break;
30419                     case 'i':
30420                         cmd = 'italic';
30421                         break;
30422                     
30423                     case 'u':
30424                         cmd = 'underline';
30425                         break;
30426                     
30427                     //case 'v':
30428                       //  this.cleanUpPaste.defer(100, this);
30429                       //  return;
30430                         
30431                 }
30432                 if(cmd){
30433                     
30434                     this.relayCmd(cmd);
30435                     //this.win.focus();
30436                     //this.execCmd(cmd);
30437                     //this.deferFocus();
30438                     e.preventDefault();
30439                 }
30440                 
30441             }
30442         }
30443     },
30444
30445     // private
30446     fixKeys : function(){ // load time branching for fastest keydown performance
30447         
30448         
30449         if(Roo.isIE){
30450             return function(e){
30451                 var k = e.getKey(), r;
30452                 if(k == e.TAB){
30453                     e.stopEvent();
30454                     r = this.doc.selection.createRange();
30455                     if(r){
30456                         r.collapse(true);
30457                         r.pasteHTML('&#160;&#160;&#160;&#160;');
30458                         this.deferFocus();
30459                     }
30460                     return;
30461                 }
30462                 /// this is handled by Roo.htmleditor.KeyEnter
30463                  /*
30464                 if(k == e.ENTER){
30465                     r = this.doc.selection.createRange();
30466                     if(r){
30467                         var target = r.parentElement();
30468                         if(!target || target.tagName.toLowerCase() != 'li'){
30469                             e.stopEvent();
30470                             r.pasteHTML('<br/>');
30471                             r.collapse(false);
30472                             r.select();
30473                         }
30474                     }
30475                 }
30476                 */
30477                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
30478                 //    this.cleanUpPaste.defer(100, this);
30479                 //    return;
30480                 //}
30481                 
30482                 
30483             };
30484         }else if(Roo.isOpera){
30485             return function(e){
30486                 var k = e.getKey();
30487                 if(k == e.TAB){
30488                     e.stopEvent();
30489                     this.win.focus();
30490                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
30491                     this.deferFocus();
30492                 }
30493                
30494                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
30495                 //    this.cleanUpPaste.defer(100, this);
30496                  //   return;
30497                 //}
30498                 
30499             };
30500         }else if(Roo.isSafari){
30501             return function(e){
30502                 var k = e.getKey();
30503                 
30504                 if(k == e.TAB){
30505                     e.stopEvent();
30506                     this.execCmd('InsertText','\t');
30507                     this.deferFocus();
30508                     return;
30509                 }
30510                  this.mozKeyPress(e);
30511                 
30512                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
30513                  //   this.cleanUpPaste.defer(100, this);
30514                  //   return;
30515                // }
30516                 
30517              };
30518         }
30519     }(),
30520     
30521     getAllAncestors: function()
30522     {
30523         var p = this.getSelectedNode();
30524         var a = [];
30525         if (!p) {
30526             a.push(p); // push blank onto stack..
30527             p = this.getParentElement();
30528         }
30529         
30530         
30531         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30532             a.push(p);
30533             p = p.parentNode;
30534         }
30535         a.push(this.doc.body);
30536         return a;
30537     },
30538     lastSel : false,
30539     lastSelNode : false,
30540     
30541     
30542     getSelection : function() 
30543     {
30544         this.assignDocWin();
30545         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30546     },
30547     /**
30548      * Select a dom node
30549      * @param {DomElement} node the node to select
30550      */
30551     selectNode : function(node, collapse)
30552     {
30553         var nodeRange = node.ownerDocument.createRange();
30554         try {
30555             nodeRange.selectNode(node);
30556         } catch (e) {
30557             nodeRange.selectNodeContents(node);
30558         }
30559         if (collapse === true) {
30560             nodeRange.collapse(true);
30561         }
30562         //
30563         var s = this.win.getSelection();
30564         s.removeAllRanges();
30565         s.addRange(nodeRange);
30566     },
30567     
30568     getSelectedNode: function() 
30569     {
30570         // this may only work on Gecko!!!
30571         
30572         // should we cache this!!!!
30573         
30574          
30575          
30576         var range = this.createRange(this.getSelection()).cloneRange();
30577         
30578         if (Roo.isIE) {
30579             var parent = range.parentElement();
30580             while (true) {
30581                 var testRange = range.duplicate();
30582                 testRange.moveToElementText(parent);
30583                 if (testRange.inRange(range)) {
30584                     break;
30585                 }
30586                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30587                     break;
30588                 }
30589                 parent = parent.parentElement;
30590             }
30591             return parent;
30592         }
30593         
30594         // is ancestor a text element.
30595         var ac =  range.commonAncestorContainer;
30596         if (ac.nodeType == 3) {
30597             ac = ac.parentNode;
30598         }
30599         
30600         var ar = ac.childNodes;
30601          
30602         var nodes = [];
30603         var other_nodes = [];
30604         var has_other_nodes = false;
30605         for (var i=0;i<ar.length;i++) {
30606             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
30607                 continue;
30608             }
30609             // fullly contained node.
30610             
30611             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30612                 nodes.push(ar[i]);
30613                 continue;
30614             }
30615             
30616             // probably selected..
30617             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30618                 other_nodes.push(ar[i]);
30619                 continue;
30620             }
30621             // outer..
30622             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
30623                 continue;
30624             }
30625             
30626             
30627             has_other_nodes = true;
30628         }
30629         if (!nodes.length && other_nodes.length) {
30630             nodes= other_nodes;
30631         }
30632         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30633             return false;
30634         }
30635         
30636         return nodes[0];
30637     },
30638     
30639     
30640     createRange: function(sel)
30641     {
30642         // this has strange effects when using with 
30643         // top toolbar - not sure if it's a great idea.
30644         //this.editor.contentWindow.focus();
30645         if (typeof sel != "undefined") {
30646             try {
30647                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30648             } catch(e) {
30649                 return this.doc.createRange();
30650             }
30651         } else {
30652             return this.doc.createRange();
30653         }
30654     },
30655     getParentElement: function()
30656     {
30657         
30658         this.assignDocWin();
30659         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30660         
30661         var range = this.createRange(sel);
30662          
30663         try {
30664             var p = range.commonAncestorContainer;
30665             while (p.nodeType == 3) { // text node
30666                 p = p.parentNode;
30667             }
30668             return p;
30669         } catch (e) {
30670             return null;
30671         }
30672     
30673     },
30674     /***
30675      *
30676      * Range intersection.. the hard stuff...
30677      *  '-1' = before
30678      *  '0' = hits..
30679      *  '1' = after.
30680      *         [ -- selected range --- ]
30681      *   [fail]                        [fail]
30682      *
30683      *    basically..
30684      *      if end is before start or  hits it. fail.
30685      *      if start is after end or hits it fail.
30686      *
30687      *   if either hits (but other is outside. - then it's not 
30688      *   
30689      *    
30690      **/
30691     
30692     
30693     // @see http://www.thismuchiknow.co.uk/?p=64.
30694     rangeIntersectsNode : function(range, node)
30695     {
30696         var nodeRange = node.ownerDocument.createRange();
30697         try {
30698             nodeRange.selectNode(node);
30699         } catch (e) {
30700             nodeRange.selectNodeContents(node);
30701         }
30702     
30703         var rangeStartRange = range.cloneRange();
30704         rangeStartRange.collapse(true);
30705     
30706         var rangeEndRange = range.cloneRange();
30707         rangeEndRange.collapse(false);
30708     
30709         var nodeStartRange = nodeRange.cloneRange();
30710         nodeStartRange.collapse(true);
30711     
30712         var nodeEndRange = nodeRange.cloneRange();
30713         nodeEndRange.collapse(false);
30714     
30715         return rangeStartRange.compareBoundaryPoints(
30716                  Range.START_TO_START, nodeEndRange) == -1 &&
30717                rangeEndRange.compareBoundaryPoints(
30718                  Range.START_TO_START, nodeStartRange) == 1;
30719         
30720          
30721     },
30722     rangeCompareNode : function(range, node)
30723     {
30724         var nodeRange = node.ownerDocument.createRange();
30725         try {
30726             nodeRange.selectNode(node);
30727         } catch (e) {
30728             nodeRange.selectNodeContents(node);
30729         }
30730         
30731         
30732         range.collapse(true);
30733     
30734         nodeRange.collapse(true);
30735      
30736         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30737         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30738          
30739         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30740         
30741         var nodeIsBefore   =  ss == 1;
30742         var nodeIsAfter    = ee == -1;
30743         
30744         if (nodeIsBefore && nodeIsAfter) {
30745             return 0; // outer
30746         }
30747         if (!nodeIsBefore && nodeIsAfter) {
30748             return 1; //right trailed.
30749         }
30750         
30751         if (nodeIsBefore && !nodeIsAfter) {
30752             return 2;  // left trailed.
30753         }
30754         // fully contined.
30755         return 3;
30756     },
30757  
30758     cleanWordChars : function(input) {// change the chars to hex code
30759         
30760        var swapCodes  = [ 
30761             [    8211, "&#8211;" ], 
30762             [    8212, "&#8212;" ], 
30763             [    8216,  "'" ],  
30764             [    8217, "'" ],  
30765             [    8220, '"' ],  
30766             [    8221, '"' ],  
30767             [    8226, "*" ],  
30768             [    8230, "..." ]
30769         ]; 
30770         var output = input;
30771         Roo.each(swapCodes, function(sw) { 
30772             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30773             
30774             output = output.replace(swapper, sw[1]);
30775         });
30776         
30777         return output;
30778     },
30779     
30780      
30781     
30782         
30783     
30784     cleanUpChild : function (node)
30785     {
30786         
30787         new Roo.htmleditor.FilterComment({node : node});
30788         new Roo.htmleditor.FilterAttributes({
30789                 node : node,
30790                 attrib_black : this.ablack,
30791                 attrib_clean : this.aclean,
30792                 style_white : this.cwhite,
30793                 style_black : this.cblack
30794         });
30795         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30796         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30797          
30798         
30799     },
30800     
30801     /**
30802      * Clean up MS wordisms...
30803      * @deprecated - use filter directly
30804      */
30805     cleanWord : function(node)
30806     {
30807         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30808         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30809         
30810     },
30811    
30812     
30813     /**
30814
30815      * @deprecated - use filters
30816      */
30817     cleanTableWidths : function(node)
30818     {
30819         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30820         
30821  
30822     },
30823     
30824      
30825         
30826     applyBlacklists : function()
30827     {
30828         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30829         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30830         
30831         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30832         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30833         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30834         
30835         this.white = [];
30836         this.black = [];
30837         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30838             if (b.indexOf(tag) > -1) {
30839                 return;
30840             }
30841             this.white.push(tag);
30842             
30843         }, this);
30844         
30845         Roo.each(w, function(tag) {
30846             if (b.indexOf(tag) > -1) {
30847                 return;
30848             }
30849             if (this.white.indexOf(tag) > -1) {
30850                 return;
30851             }
30852             this.white.push(tag);
30853             
30854         }, this);
30855         
30856         
30857         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30858             if (w.indexOf(tag) > -1) {
30859                 return;
30860             }
30861             this.black.push(tag);
30862             
30863         }, this);
30864         
30865         Roo.each(b, function(tag) {
30866             if (w.indexOf(tag) > -1) {
30867                 return;
30868             }
30869             if (this.black.indexOf(tag) > -1) {
30870                 return;
30871             }
30872             this.black.push(tag);
30873             
30874         }, this);
30875         
30876         
30877         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30878         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30879         
30880         this.cwhite = [];
30881         this.cblack = [];
30882         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30883             if (b.indexOf(tag) > -1) {
30884                 return;
30885             }
30886             this.cwhite.push(tag);
30887             
30888         }, this);
30889         
30890         Roo.each(w, function(tag) {
30891             if (b.indexOf(tag) > -1) {
30892                 return;
30893             }
30894             if (this.cwhite.indexOf(tag) > -1) {
30895                 return;
30896             }
30897             this.cwhite.push(tag);
30898             
30899         }, this);
30900         
30901         
30902         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30903             if (w.indexOf(tag) > -1) {
30904                 return;
30905             }
30906             this.cblack.push(tag);
30907             
30908         }, this);
30909         
30910         Roo.each(b, function(tag) {
30911             if (w.indexOf(tag) > -1) {
30912                 return;
30913             }
30914             if (this.cblack.indexOf(tag) > -1) {
30915                 return;
30916             }
30917             this.cblack.push(tag);
30918             
30919         }, this);
30920     },
30921     
30922     setStylesheets : function(stylesheets)
30923     {
30924         if(typeof(stylesheets) == 'string'){
30925             Roo.get(this.iframe.contentDocument.head).createChild({
30926                 tag : 'link',
30927                 rel : 'stylesheet',
30928                 type : 'text/css',
30929                 href : stylesheets
30930             });
30931             
30932             return;
30933         }
30934         var _this = this;
30935      
30936         Roo.each(stylesheets, function(s) {
30937             if(!s.length){
30938                 return;
30939             }
30940             
30941             Roo.get(_this.iframe.contentDocument.head).createChild({
30942                 tag : 'link',
30943                 rel : 'stylesheet',
30944                 type : 'text/css',
30945                 href : s
30946             });
30947         });
30948
30949         
30950     },
30951     
30952     
30953     updateLanguage : function()
30954     {
30955         if (!this.iframe || !this.iframe.contentDocument) {
30956             return;
30957         }
30958         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30959     },
30960     
30961     
30962     removeStylesheets : function()
30963     {
30964         var _this = this;
30965         
30966         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30967             s.remove();
30968         });
30969     },
30970     
30971     setStyle : function(style)
30972     {
30973         Roo.get(this.iframe.contentDocument.head).createChild({
30974             tag : 'style',
30975             type : 'text/css',
30976             html : style
30977         });
30978
30979         return;
30980     }
30981     
30982     // hide stuff that is not compatible
30983     /**
30984      * @event blur
30985      * @hide
30986      */
30987     /**
30988      * @event change
30989      * @hide
30990      */
30991     /**
30992      * @event focus
30993      * @hide
30994      */
30995     /**
30996      * @event specialkey
30997      * @hide
30998      */
30999     /**
31000      * @cfg {String} fieldClass @hide
31001      */
31002     /**
31003      * @cfg {String} focusClass @hide
31004      */
31005     /**
31006      * @cfg {String} autoCreate @hide
31007      */
31008     /**
31009      * @cfg {String} inputType @hide
31010      */
31011     /**
31012      * @cfg {String} invalidClass @hide
31013      */
31014     /**
31015      * @cfg {String} invalidText @hide
31016      */
31017     /**
31018      * @cfg {String} msgFx @hide
31019      */
31020     /**
31021      * @cfg {String} validateOnBlur @hide
31022      */
31023 });
31024
31025 Roo.HtmlEditorCore.white = [
31026         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31027         
31028        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31029        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31030        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31031        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31032        'TABLE',   'UL',         'XMP', 
31033        
31034        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31035       'THEAD',   'TR', 
31036      
31037       'DIR', 'MENU', 'OL', 'UL', 'DL',
31038        
31039       'EMBED',  'OBJECT'
31040 ];
31041
31042
31043 Roo.HtmlEditorCore.black = [
31044     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31045         'APPLET', // 
31046         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31047         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31048         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31049         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31050         //'FONT' // CLEAN LATER..
31051         'COLGROUP', 'COL'   // messy tables.
31052         
31053         
31054 ];
31055 Roo.HtmlEditorCore.clean = [ // ?? needed???
31056      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31057 ];
31058 Roo.HtmlEditorCore.tag_remove = [
31059     'FONT', 'TBODY'  
31060 ];
31061 // attributes..
31062
31063 Roo.HtmlEditorCore.ablack = [
31064     'on'
31065 ];
31066     
31067 Roo.HtmlEditorCore.aclean = [ 
31068     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31069 ];
31070
31071 // protocols..
31072 Roo.HtmlEditorCore.pwhite= [
31073         'http',  'https',  'mailto'
31074 ];
31075
31076 // white listed style attributes.
31077 Roo.HtmlEditorCore.cwhite= [
31078       //  'text-align', /// default is to allow most things..
31079       
31080          
31081 //        'font-size'//??
31082 ];
31083
31084 // black listed style attributes.
31085 Roo.HtmlEditorCore.cblack= [
31086       //  'font-size' -- this can be set by the project 
31087 ];
31088
31089
31090
31091
31092     /*
31093  * - LGPL
31094  *
31095  * HtmlEditor
31096  * 
31097  */
31098
31099 /**
31100  * @class Roo.bootstrap.form.HtmlEditor
31101  * @extends Roo.bootstrap.form.TextArea
31102  * Bootstrap HtmlEditor class
31103
31104  * @constructor
31105  * Create a new HtmlEditor
31106  * @param {Object} config The config object
31107  */
31108
31109 Roo.bootstrap.form.HtmlEditor = function(config){
31110     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31111     if (!this.toolbars) {
31112         this.toolbars = [];
31113     }
31114     
31115     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31116     this.addEvents({
31117             /**
31118              * @event initialize
31119              * Fires when the editor is fully initialized (including the iframe)
31120              * @param {HtmlEditor} this
31121              */
31122             initialize: true,
31123             /**
31124              * @event activate
31125              * Fires when the editor is first receives the focus. Any insertion must wait
31126              * until after this event.
31127              * @param {HtmlEditor} this
31128              */
31129             activate: true,
31130              /**
31131              * @event beforesync
31132              * Fires before the textarea is updated with content from the editor iframe. Return false
31133              * to cancel the sync.
31134              * @param {HtmlEditor} this
31135              * @param {String} html
31136              */
31137             beforesync: true,
31138              /**
31139              * @event beforepush
31140              * Fires before the iframe editor is updated with content from the textarea. Return false
31141              * to cancel the push.
31142              * @param {HtmlEditor} this
31143              * @param {String} html
31144              */
31145             beforepush: true,
31146              /**
31147              * @event sync
31148              * Fires when the textarea is updated with content from the editor iframe.
31149              * @param {HtmlEditor} this
31150              * @param {String} html
31151              */
31152             sync: true,
31153              /**
31154              * @event push
31155              * Fires when the iframe editor is updated with content from the textarea.
31156              * @param {HtmlEditor} this
31157              * @param {String} html
31158              */
31159             push: true,
31160              /**
31161              * @event editmodechange
31162              * Fires when the editor switches edit modes
31163              * @param {HtmlEditor} this
31164              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31165              */
31166             editmodechange: true,
31167             /**
31168              * @event editorevent
31169              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31170              * @param {HtmlEditor} this
31171              */
31172             editorevent: true,
31173             /**
31174              * @event firstfocus
31175              * Fires when on first focus - needed by toolbars..
31176              * @param {HtmlEditor} this
31177              */
31178             firstfocus: true,
31179             /**
31180              * @event autosave
31181              * Auto save the htmlEditor value as a file into Events
31182              * @param {HtmlEditor} this
31183              */
31184             autosave: true,
31185             /**
31186              * @event savedpreview
31187              * preview the saved version of htmlEditor
31188              * @param {HtmlEditor} this
31189              */
31190             savedpreview: true
31191         });
31192 };
31193
31194
31195 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31196     
31197     
31198       /**
31199      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31200      */
31201     toolbars : false,
31202     
31203      /**
31204     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
31205     */
31206     btns : [],
31207    
31208      /**
31209      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
31210      *                        Roo.resizable.
31211      */
31212     resizable : false,
31213      /**
31214      * @cfg {Number} height (in pixels)
31215      */   
31216     height: 300,
31217    /**
31218      * @cfg {Number} width (in pixels)
31219      */   
31220     width: false,
31221     
31222     /**
31223      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31224      * 
31225      */
31226     stylesheets: false,
31227     
31228     // id of frame..
31229     frameId: false,
31230     
31231     // private properties
31232     validationEvent : false,
31233     deferHeight: true,
31234     initialized : false,
31235     activated : false,
31236     
31237     onFocus : Roo.emptyFn,
31238     iframePad:3,
31239     hideMode:'offsets',
31240     
31241     tbContainer : false,
31242     
31243     bodyCls : '',
31244     
31245     toolbarContainer :function() {
31246         return this.wrap.select('.x-html-editor-tb',true).first();
31247     },
31248
31249     /**
31250      * Protected method that will not generally be called directly. It
31251      * is called when the editor creates its toolbar. Override this method if you need to
31252      * add custom toolbar buttons.
31253      * @param {HtmlEditor} editor
31254      */
31255     createToolbar : function(){
31256         Roo.log('renewing');
31257         Roo.log("create toolbars");
31258         
31259         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
31260         this.toolbars[0].render(this.toolbarContainer());
31261         
31262         return;
31263         
31264 //        if (!editor.toolbars || !editor.toolbars.length) {
31265 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
31266 //        }
31267 //        
31268 //        for (var i =0 ; i < editor.toolbars.length;i++) {
31269 //            editor.toolbars[i] = Roo.factory(
31270 //                    typeof(editor.toolbars[i]) == 'string' ?
31271 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
31272 //                Roo.bootstrap.form.HtmlEditor);
31273 //            editor.toolbars[i].init(editor);
31274 //        }
31275     },
31276
31277      
31278     // private
31279     onRender : function(ct, position)
31280     {
31281        // Roo.log("Call onRender: " + this.xtype);
31282         var _t = this;
31283         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
31284       
31285         this.wrap = this.inputEl().wrap({
31286             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
31287         });
31288         
31289         this.editorcore.onRender(ct, position);
31290          
31291         if (this.resizable) {
31292             this.resizeEl = new Roo.Resizable(this.wrap, {
31293                 pinned : true,
31294                 wrap: true,
31295                 dynamic : true,
31296                 minHeight : this.height,
31297                 height: this.height,
31298                 handles : this.resizable,
31299                 width: this.width,
31300                 listeners : {
31301                     resize : function(r, w, h) {
31302                         _t.onResize(w,h); // -something
31303                     }
31304                 }
31305             });
31306             
31307         }
31308         this.createToolbar(this);
31309        
31310         
31311         if(!this.width && this.resizable){
31312             this.setSize(this.wrap.getSize());
31313         }
31314         if (this.resizeEl) {
31315             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
31316             // should trigger onReize..
31317         }
31318         
31319     },
31320
31321     // private
31322     onResize : function(w, h)
31323     {
31324         Roo.log('resize: ' +w + ',' + h );
31325         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
31326         var ew = false;
31327         var eh = false;
31328         
31329         if(this.inputEl() ){
31330             if(typeof w == 'number'){
31331                 var aw = w - this.wrap.getFrameWidth('lr');
31332                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
31333                 ew = aw;
31334             }
31335             if(typeof h == 'number'){
31336                  var tbh = -11;  // fixme it needs to tool bar size!
31337                 for (var i =0; i < this.toolbars.length;i++) {
31338                     // fixme - ask toolbars for heights?
31339                     tbh += this.toolbars[i].el.getHeight();
31340                     //if (this.toolbars[i].footer) {
31341                     //    tbh += this.toolbars[i].footer.el.getHeight();
31342                     //}
31343                 }
31344               
31345                 
31346                 
31347                 
31348                 
31349                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
31350                 ah -= 5; // knock a few pixes off for look..
31351                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
31352                 var eh = ah;
31353             }
31354         }
31355         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
31356         this.editorcore.onResize(ew,eh);
31357         
31358     },
31359
31360     /**
31361      * Toggles the editor between standard and source edit mode.
31362      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31363      */
31364     toggleSourceEdit : function(sourceEditMode)
31365     {
31366         this.editorcore.toggleSourceEdit(sourceEditMode);
31367         
31368         if(this.editorcore.sourceEditMode){
31369             Roo.log('editor - showing textarea');
31370             
31371 //            Roo.log('in');
31372 //            Roo.log(this.syncValue());
31373             this.syncValue();
31374             this.inputEl().removeClass(['hide', 'x-hidden']);
31375             this.inputEl().dom.removeAttribute('tabIndex');
31376             this.inputEl().focus();
31377         }else{
31378             Roo.log('editor - hiding textarea');
31379 //            Roo.log('out')
31380 //            Roo.log(this.pushValue()); 
31381             this.pushValue();
31382             
31383             this.inputEl().addClass(['hide', 'x-hidden']);
31384             this.inputEl().dom.setAttribute('tabIndex', -1);
31385             //this.deferFocus();
31386         }
31387          
31388         if(this.resizable){
31389             this.setSize(this.wrap.getSize());
31390         }
31391         
31392         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
31393     },
31394  
31395     // private (for BoxComponent)
31396     adjustSize : Roo.BoxComponent.prototype.adjustSize,
31397
31398     // private (for BoxComponent)
31399     getResizeEl : function(){
31400         return this.wrap;
31401     },
31402
31403     // private (for BoxComponent)
31404     getPositionEl : function(){
31405         return this.wrap;
31406     },
31407
31408     // private
31409     initEvents : function(){
31410         this.originalValue = this.getValue();
31411     },
31412
31413 //    /**
31414 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
31415 //     * @method
31416 //     */
31417 //    markInvalid : Roo.emptyFn,
31418 //    /**
31419 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
31420 //     * @method
31421 //     */
31422 //    clearInvalid : Roo.emptyFn,
31423
31424     setValue : function(v){
31425         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
31426         this.editorcore.pushValue();
31427     },
31428
31429      
31430     // private
31431     deferFocus : function(){
31432         this.focus.defer(10, this);
31433     },
31434
31435     // doc'ed in Field
31436     focus : function(){
31437         this.editorcore.focus();
31438         
31439     },
31440       
31441
31442     // private
31443     onDestroy : function(){
31444         
31445         
31446         
31447         if(this.rendered){
31448             
31449             for (var i =0; i < this.toolbars.length;i++) {
31450                 // fixme - ask toolbars for heights?
31451                 this.toolbars[i].onDestroy();
31452             }
31453             
31454             this.wrap.dom.innerHTML = '';
31455             this.wrap.remove();
31456         }
31457     },
31458
31459     // private
31460     onFirstFocus : function(){
31461         //Roo.log("onFirstFocus");
31462         this.editorcore.onFirstFocus();
31463          for (var i =0; i < this.toolbars.length;i++) {
31464             this.toolbars[i].onFirstFocus();
31465         }
31466         
31467     },
31468     
31469     // private
31470     syncValue : function()
31471     {   
31472         this.editorcore.syncValue();
31473     },
31474     
31475     pushValue : function()
31476     {   
31477         this.editorcore.pushValue();
31478     }
31479      
31480     
31481     // hide stuff that is not compatible
31482     /**
31483      * @event blur
31484      * @hide
31485      */
31486     /**
31487      * @event change
31488      * @hide
31489      */
31490     /**
31491      * @event focus
31492      * @hide
31493      */
31494     /**
31495      * @event specialkey
31496      * @hide
31497      */
31498     /**
31499      * @cfg {String} fieldClass @hide
31500      */
31501     /**
31502      * @cfg {String} focusClass @hide
31503      */
31504     /**
31505      * @cfg {String} autoCreate @hide
31506      */
31507     /**
31508      * @cfg {String} inputType @hide
31509      */
31510      
31511     /**
31512      * @cfg {String} invalidText @hide
31513      */
31514     /**
31515      * @cfg {String} msgFx @hide
31516      */
31517     /**
31518      * @cfg {String} validateOnBlur @hide
31519      */
31520 });
31521  
31522     
31523    
31524    
31525    
31526       
31527 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31528 /**
31529  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31530  * @parent Roo.bootstrap.form.HtmlEditor
31531  * @extends Roo.bootstrap.nav.Simplebar
31532  * Basic Toolbar
31533  * 
31534  * @example
31535  * Usage:
31536  *
31537  new Roo.bootstrap.form.HtmlEditor({
31538     ....
31539     toolbars : [
31540         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31541             disable : { fonts: 1 , format: 1, ..., ... , ...],
31542             btns : [ .... ]
31543         })
31544     }
31545      
31546  * 
31547  * @cfg {Object} disable List of elements to disable..
31548  * @cfg {Array} btns List of additional buttons.
31549  * 
31550  * 
31551  * NEEDS Extra CSS? 
31552  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31553  */
31554  
31555 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31556 {
31557     
31558     Roo.apply(this, config);
31559     
31560     // default disabled, based on 'good practice'..
31561     this.disable = this.disable || {};
31562     Roo.applyIf(this.disable, {
31563         fontSize : true,
31564         colors : true,
31565         specialElements : true
31566     });
31567     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31568     
31569     this.editor = config.editor;
31570     this.editorcore = config.editor.editorcore;
31571     
31572     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31573     
31574     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31575     // dont call parent... till later.
31576 }
31577 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
31578      
31579     bar : true,
31580     
31581     editor : false,
31582     editorcore : false,
31583     
31584     
31585     formats : [
31586         "p" ,  
31587         "h1","h2","h3","h4","h5","h6", 
31588         "pre", "code", 
31589         "abbr", "acronym", "address", "cite", "samp", "var",
31590         'div','span'
31591     ],
31592     
31593     onRender : function(ct, position)
31594     {
31595        // Roo.log("Call onRender: " + this.xtype);
31596         
31597        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31598        Roo.log(this.el);
31599        this.el.dom.style.marginBottom = '0';
31600        var _this = this;
31601        var editorcore = this.editorcore;
31602        var editor= this.editor;
31603        
31604        var children = [];
31605        var btn = function(id,cmd , toggle, handler, html){
31606        
31607             var  event = toggle ? 'toggle' : 'click';
31608        
31609             var a = {
31610                 size : 'sm',
31611                 xtype: 'Button',
31612                 xns: Roo.bootstrap,
31613                 //glyphicon : id,
31614                 fa: id,
31615                 cmd : id || cmd,
31616                 enableToggle:toggle !== false,
31617                 html : html || '',
31618                 pressed : toggle ? false : null,
31619                 listeners : {}
31620             };
31621             a.listeners[toggle ? 'toggle' : 'click'] = function() {
31622                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
31623             };
31624             children.push(a);
31625             return a;
31626        }
31627        
31628     //    var cb_box = function...
31629         
31630         var style = {
31631                 xtype: 'Button',
31632                 size : 'sm',
31633                 xns: Roo.bootstrap,
31634                 fa : 'font',
31635                 //html : 'submit'
31636                 menu : {
31637                     xtype: 'Menu',
31638                     xns: Roo.bootstrap,
31639                     items:  []
31640                 }
31641         };
31642         Roo.each(this.formats, function(f) {
31643             style.menu.items.push({
31644                 xtype :'MenuItem',
31645                 xns: Roo.bootstrap,
31646                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31647                 tagname : f,
31648                 listeners : {
31649                     click : function()
31650                     {
31651                         editorcore.insertTag(this.tagname);
31652                         editor.focus();
31653                     }
31654                 }
31655                 
31656             });
31657         });
31658         children.push(style);   
31659         
31660         btn('bold',false,true);
31661         btn('italic',false,true);
31662         btn('align-left', 'justifyleft',true);
31663         btn('align-center', 'justifycenter',true);
31664         btn('align-right' , 'justifyright',true);
31665         btn('link', false, false, function(btn) {
31666             //Roo.log("create link?");
31667             var url = prompt(this.createLinkText, this.defaultLinkValue);
31668             if(url && url != 'http:/'+'/'){
31669                 this.editorcore.relayCmd('createlink', url);
31670             }
31671         }),
31672         btn('list','insertunorderedlist',true);
31673         btn('pencil', false,true, function(btn){
31674                 Roo.log(this);
31675                 this.toggleSourceEdit(btn.pressed);
31676         });
31677         
31678         if (this.editor.btns.length > 0) {
31679             for (var i = 0; i<this.editor.btns.length; i++) {
31680                 children.push(this.editor.btns[i]);
31681             }
31682         }
31683         
31684         /*
31685         var cog = {
31686                 xtype: 'Button',
31687                 size : 'sm',
31688                 xns: Roo.bootstrap,
31689                 glyphicon : 'cog',
31690                 //html : 'submit'
31691                 menu : {
31692                     xtype: 'Menu',
31693                     xns: Roo.bootstrap,
31694                     items:  []
31695                 }
31696         };
31697         
31698         cog.menu.items.push({
31699             xtype :'MenuItem',
31700             xns: Roo.bootstrap,
31701             html : Clean styles,
31702             tagname : f,
31703             listeners : {
31704                 click : function()
31705                 {
31706                     editorcore.insertTag(this.tagname);
31707                     editor.focus();
31708                 }
31709             }
31710             
31711         });
31712        */
31713         
31714          
31715        this.xtype = 'NavSimplebar';
31716         
31717         for(var i=0;i< children.length;i++) {
31718             
31719             this.buttons.add(this.addxtypeChild(children[i]));
31720             
31721         }
31722         
31723         editor.on('editorevent', this.updateToolbar, this);
31724     },
31725     onBtnClick : function(id)
31726     {
31727        this.editorcore.relayCmd(id);
31728        this.editorcore.focus();
31729     },
31730     
31731     /**
31732      * Protected method that will not generally be called directly. It triggers
31733      * a toolbar update by reading the markup state of the current selection in the editor.
31734      */
31735     updateToolbar: function(){
31736
31737         if(!this.editorcore.activated){
31738             this.editor.onFirstFocus(); // is this neeed?
31739             return;
31740         }
31741
31742         var btns = this.buttons; 
31743         var doc = this.editorcore.doc;
31744         btns.get('bold').setActive(doc.queryCommandState('bold'));
31745         btns.get('italic').setActive(doc.queryCommandState('italic'));
31746         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31747         
31748         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31749         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31750         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31751         
31752         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31753         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31754          /*
31755         
31756         var ans = this.editorcore.getAllAncestors();
31757         if (this.formatCombo) {
31758             
31759             
31760             var store = this.formatCombo.store;
31761             this.formatCombo.setValue("");
31762             for (var i =0; i < ans.length;i++) {
31763                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31764                     // select it..
31765                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31766                     break;
31767                 }
31768             }
31769         }
31770         
31771         
31772         
31773         // hides menus... - so this cant be on a menu...
31774         Roo.bootstrap.MenuMgr.hideAll();
31775         */
31776         Roo.bootstrap.menu.Manager.hideAll();
31777         //this.editorsyncValue();
31778     },
31779     onFirstFocus: function() {
31780         this.buttons.each(function(item){
31781            item.enable();
31782         });
31783     },
31784     toggleSourceEdit : function(sourceEditMode){
31785         
31786           
31787         if(sourceEditMode){
31788             Roo.log("disabling buttons");
31789            this.buttons.each( function(item){
31790                 if(item.cmd != 'pencil'){
31791                     item.disable();
31792                 }
31793             });
31794           
31795         }else{
31796             Roo.log("enabling buttons");
31797             if(this.editorcore.initialized){
31798                 this.buttons.each( function(item){
31799                     item.enable();
31800                 });
31801             }
31802             
31803         }
31804         Roo.log("calling toggole on editor");
31805         // tell the editor that it's been pressed..
31806         this.editor.toggleSourceEdit(sourceEditMode);
31807        
31808     }
31809 });
31810
31811
31812
31813
31814  
31815 /*
31816  * - LGPL
31817  */
31818
31819 /**
31820  * @class Roo.bootstrap.form.Markdown
31821  * @extends Roo.bootstrap.form.TextArea
31822  * Bootstrap Showdown editable area
31823  * @cfg {string} content
31824  * 
31825  * @constructor
31826  * Create a new Showdown
31827  */
31828
31829 Roo.bootstrap.form.Markdown = function(config){
31830     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31831    
31832 };
31833
31834 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31835     
31836     editing :false,
31837     
31838     initEvents : function()
31839     {
31840         
31841         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31842         this.markdownEl = this.el.createChild({
31843             cls : 'roo-markdown-area'
31844         });
31845         this.inputEl().addClass('d-none');
31846         if (this.getValue() == '') {
31847             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31848             
31849         } else {
31850             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31851         }
31852         this.markdownEl.on('click', this.toggleTextEdit, this);
31853         this.on('blur', this.toggleTextEdit, this);
31854         this.on('specialkey', this.resizeTextArea, this);
31855     },
31856     
31857     toggleTextEdit : function()
31858     {
31859         var sh = this.markdownEl.getHeight();
31860         this.inputEl().addClass('d-none');
31861         this.markdownEl.addClass('d-none');
31862         if (!this.editing) {
31863             // show editor?
31864             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31865             this.inputEl().removeClass('d-none');
31866             this.inputEl().focus();
31867             this.editing = true;
31868             return;
31869         }
31870         // show showdown...
31871         this.updateMarkdown();
31872         this.markdownEl.removeClass('d-none');
31873         this.editing = false;
31874         return;
31875     },
31876     updateMarkdown : function()
31877     {
31878         if (this.getValue() == '') {
31879             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31880             return;
31881         }
31882  
31883         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31884     },
31885     
31886     resizeTextArea: function () {
31887         
31888         var sh = 100;
31889         Roo.log([sh, this.getValue().split("\n").length * 30]);
31890         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31891     },
31892     setValue : function(val)
31893     {
31894         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31895         if (!this.editing) {
31896             this.updateMarkdown();
31897         }
31898         
31899     },
31900     focus : function()
31901     {
31902         if (!this.editing) {
31903             this.toggleTextEdit();
31904         }
31905         
31906     }
31907
31908
31909 });/*
31910  * Based on:
31911  * Ext JS Library 1.1.1
31912  * Copyright(c) 2006-2007, Ext JS, LLC.
31913  *
31914  * Originally Released Under LGPL - original licence link has changed is not relivant.
31915  *
31916  * Fork - LGPL
31917  * <script type="text/javascript">
31918  */
31919  
31920 /**
31921  * @class Roo.bootstrap.PagingToolbar
31922  * @extends Roo.bootstrap.nav.Simplebar
31923  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31924  * @constructor
31925  * Create a new PagingToolbar
31926  * @param {Object} config The config object
31927  * @param {Roo.data.Store} store
31928  */
31929 Roo.bootstrap.PagingToolbar = function(config)
31930 {
31931     // old args format still supported... - xtype is prefered..
31932         // created from xtype...
31933     
31934     this.ds = config.dataSource;
31935     
31936     if (config.store && !this.ds) {
31937         this.store= Roo.factory(config.store, Roo.data);
31938         this.ds = this.store;
31939         this.ds.xmodule = this.xmodule || false;
31940     }
31941     
31942     this.toolbarItems = [];
31943     if (config.items) {
31944         this.toolbarItems = config.items;
31945     }
31946     
31947     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31948     
31949     this.cursor = 0;
31950     
31951     if (this.ds) { 
31952         this.bind(this.ds);
31953     }
31954     
31955     if (Roo.bootstrap.version == 4) {
31956         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31957     } else {
31958         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31959     }
31960     
31961 };
31962
31963 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31964     /**
31965      * @cfg {Roo.bootstrap.Button} buttons[]
31966      * Buttons for the toolbar
31967      */
31968      /**
31969      * @cfg {Roo.data.Store} store
31970      * The underlying data store providing the paged data
31971      */
31972     /**
31973      * @cfg {String/HTMLElement/Element} container
31974      * container The id or element that will contain the toolbar
31975      */
31976     /**
31977      * @cfg {Boolean} displayInfo
31978      * True to display the displayMsg (defaults to false)
31979      */
31980     /**
31981      * @cfg {Number} pageSize
31982      * The number of records to display per page (defaults to 20)
31983      */
31984     pageSize: 20,
31985     /**
31986      * @cfg {String} displayMsg
31987      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31988      */
31989     displayMsg : 'Displaying {0} - {1} of {2}',
31990     /**
31991      * @cfg {String} emptyMsg
31992      * The message to display when no records are found (defaults to "No data to display")
31993      */
31994     emptyMsg : 'No data to display',
31995     /**
31996      * Customizable piece of the default paging text (defaults to "Page")
31997      * @type String
31998      */
31999     beforePageText : "Page",
32000     /**
32001      * Customizable piece of the default paging text (defaults to "of %0")
32002      * @type String
32003      */
32004     afterPageText : "of {0}",
32005     /**
32006      * Customizable piece of the default paging text (defaults to "First Page")
32007      * @type String
32008      */
32009     firstText : "First Page",
32010     /**
32011      * Customizable piece of the default paging text (defaults to "Previous Page")
32012      * @type String
32013      */
32014     prevText : "Previous Page",
32015     /**
32016      * Customizable piece of the default paging text (defaults to "Next Page")
32017      * @type String
32018      */
32019     nextText : "Next Page",
32020     /**
32021      * Customizable piece of the default paging text (defaults to "Last Page")
32022      * @type String
32023      */
32024     lastText : "Last Page",
32025     /**
32026      * Customizable piece of the default paging text (defaults to "Refresh")
32027      * @type String
32028      */
32029     refreshText : "Refresh",
32030
32031     buttons : false,
32032     // private
32033     onRender : function(ct, position) 
32034     {
32035         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32036         this.navgroup.parentId = this.id;
32037         this.navgroup.onRender(this.el, null);
32038         // add the buttons to the navgroup
32039         
32040         if(this.displayInfo){
32041             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32042             this.displayEl = this.el.select('.x-paging-info', true).first();
32043 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32044 //            this.displayEl = navel.el.select('span',true).first();
32045         }
32046         
32047         var _this = this;
32048         
32049         if(this.buttons){
32050             Roo.each(_this.buttons, function(e){ // this might need to use render????
32051                Roo.factory(e).render(_this.el);
32052             });
32053         }
32054             
32055         Roo.each(_this.toolbarItems, function(e) {
32056             _this.navgroup.addItem(e);
32057         });
32058         
32059         
32060         this.first = this.navgroup.addItem({
32061             tooltip: this.firstText,
32062             cls: "prev btn-outline-secondary",
32063             html : ' <i class="fa fa-step-backward"></i>',
32064             disabled: true,
32065             preventDefault: true,
32066             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32067         });
32068         
32069         this.prev =  this.navgroup.addItem({
32070             tooltip: this.prevText,
32071             cls: "prev btn-outline-secondary",
32072             html : ' <i class="fa fa-backward"></i>',
32073             disabled: true,
32074             preventDefault: true,
32075             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32076         });
32077     //this.addSeparator();
32078         
32079         
32080         var field = this.navgroup.addItem( {
32081             tagtype : 'span',
32082             cls : 'x-paging-position  btn-outline-secondary',
32083              disabled: true,
32084             html : this.beforePageText  +
32085                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32086                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32087          } ); //?? escaped?
32088         
32089         this.field = field.el.select('input', true).first();
32090         this.field.on("keydown", this.onPagingKeydown, this);
32091         this.field.on("focus", function(){this.dom.select();});
32092     
32093     
32094         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32095         //this.field.setHeight(18);
32096         //this.addSeparator();
32097         this.next = this.navgroup.addItem({
32098             tooltip: this.nextText,
32099             cls: "next btn-outline-secondary",
32100             html : ' <i class="fa fa-forward"></i>',
32101             disabled: true,
32102             preventDefault: true,
32103             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32104         });
32105         this.last = this.navgroup.addItem({
32106             tooltip: this.lastText,
32107             html : ' <i class="fa fa-step-forward"></i>',
32108             cls: "next btn-outline-secondary",
32109             disabled: true,
32110             preventDefault: true,
32111             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32112         });
32113     //this.addSeparator();
32114         this.loading = this.navgroup.addItem({
32115             tooltip: this.refreshText,
32116             cls: "btn-outline-secondary",
32117             html : ' <i class="fa fa-refresh"></i>',
32118             preventDefault: true,
32119             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32120         });
32121         
32122     },
32123
32124     // private
32125     updateInfo : function(){
32126         if(this.displayEl){
32127             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32128             var msg = count == 0 ?
32129                 this.emptyMsg :
32130                 String.format(
32131                     this.displayMsg,
32132                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32133                 );
32134             this.displayEl.update(msg);
32135         }
32136     },
32137
32138     // private
32139     onLoad : function(ds, r, o)
32140     {
32141         this.cursor = o.params && o.params.start ? o.params.start : 0;
32142         
32143         var d = this.getPageData(),
32144             ap = d.activePage,
32145             ps = d.pages;
32146         
32147         
32148         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32149         this.field.dom.value = ap;
32150         this.first.setDisabled(ap == 1);
32151         this.prev.setDisabled(ap == 1);
32152         this.next.setDisabled(ap == ps);
32153         this.last.setDisabled(ap == ps);
32154         this.loading.enable();
32155         this.updateInfo();
32156     },
32157
32158     // private
32159     getPageData : function(){
32160         var total = this.ds.getTotalCount();
32161         return {
32162             total : total,
32163             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32164             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32165         };
32166     },
32167
32168     // private
32169     onLoadError : function(proxy, o){
32170         this.loading.enable();
32171         if (this.ds.events.loadexception.listeners.length  < 2) {
32172             // nothing has been assigned to loadexception except this...
32173             // so 
32174             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32175
32176         }
32177     },
32178
32179     // private
32180     onPagingKeydown : function(e){
32181         var k = e.getKey();
32182         var d = this.getPageData();
32183         if(k == e.RETURN){
32184             var v = this.field.dom.value, pageNum;
32185             if(!v || isNaN(pageNum = parseInt(v, 10))){
32186                 this.field.dom.value = d.activePage;
32187                 return;
32188             }
32189             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32190             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32191             e.stopEvent();
32192         }
32193         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))
32194         {
32195           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32196           this.field.dom.value = pageNum;
32197           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32198           e.stopEvent();
32199         }
32200         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32201         {
32202           var v = this.field.dom.value, pageNum; 
32203           var increment = (e.shiftKey) ? 10 : 1;
32204           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32205                 increment *= -1;
32206           }
32207           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32208             this.field.dom.value = d.activePage;
32209             return;
32210           }
32211           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32212           {
32213             this.field.dom.value = parseInt(v, 10) + increment;
32214             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32215             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32216           }
32217           e.stopEvent();
32218         }
32219     },
32220
32221     // private
32222     beforeLoad : function(){
32223         if(this.loading){
32224             this.loading.disable();
32225         }
32226     },
32227
32228     // private
32229     onClick : function(which){
32230         
32231         var ds = this.ds;
32232         if (!ds) {
32233             return;
32234         }
32235         
32236         switch(which){
32237             case "first":
32238                 ds.load({params:{start: 0, limit: this.pageSize}});
32239             break;
32240             case "prev":
32241                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32242             break;
32243             case "next":
32244                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32245             break;
32246             case "last":
32247                 var total = ds.getTotalCount();
32248                 var extra = total % this.pageSize;
32249                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32250                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32251             break;
32252             case "refresh":
32253                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32254             break;
32255         }
32256     },
32257
32258     /**
32259      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32260      * @param {Roo.data.Store} store The data store to unbind
32261      */
32262     unbind : function(ds){
32263         ds.un("beforeload", this.beforeLoad, this);
32264         ds.un("load", this.onLoad, this);
32265         ds.un("loadexception", this.onLoadError, this);
32266         ds.un("remove", this.updateInfo, this);
32267         ds.un("add", this.updateInfo, this);
32268         this.ds = undefined;
32269     },
32270
32271     /**
32272      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32273      * @param {Roo.data.Store} store The data store to bind
32274      */
32275     bind : function(ds){
32276         ds.on("beforeload", this.beforeLoad, this);
32277         ds.on("load", this.onLoad, this);
32278         ds.on("loadexception", this.onLoadError, this);
32279         ds.on("remove", this.updateInfo, this);
32280         ds.on("add", this.updateInfo, this);
32281         this.ds = ds;
32282     }
32283 });/*
32284  * - LGPL
32285  *
32286  * element
32287  * 
32288  */
32289
32290 /**
32291  * @class Roo.bootstrap.MessageBar
32292  * @extends Roo.bootstrap.Component
32293  * Bootstrap MessageBar class
32294  * @cfg {String} html contents of the MessageBar
32295  * @cfg {String} weight (info | success | warning | danger) default info
32296  * @cfg {String} beforeClass insert the bar before the given class
32297  * @cfg {Boolean} closable (true | false) default false
32298  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
32299  * 
32300  * @constructor
32301  * Create a new Element
32302  * @param {Object} config The config object
32303  */
32304
32305 Roo.bootstrap.MessageBar = function(config){
32306     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
32307 };
32308
32309 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
32310     
32311     html: '',
32312     weight: 'info',
32313     closable: false,
32314     fixed: false,
32315     beforeClass: 'bootstrap-sticky-wrap',
32316     
32317     getAutoCreate : function(){
32318         
32319         var cfg = {
32320             tag: 'div',
32321             cls: 'alert alert-dismissable alert-' + this.weight,
32322             cn: [
32323                 {
32324                     tag: 'span',
32325                     cls: 'message',
32326                     html: this.html || ''
32327                 }
32328             ]
32329         };
32330         
32331         if(this.fixed){
32332             cfg.cls += ' alert-messages-fixed';
32333         }
32334         
32335         if(this.closable){
32336             cfg.cn.push({
32337                 tag: 'button',
32338                 cls: 'close',
32339                 html: 'x'
32340             });
32341         }
32342         
32343         return cfg;
32344     },
32345     
32346     onRender : function(ct, position)
32347     {
32348         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
32349         
32350         if(!this.el){
32351             var cfg = Roo.apply({},  this.getAutoCreate());
32352             cfg.id = Roo.id();
32353             
32354             if (this.cls) {
32355                 cfg.cls += ' ' + this.cls;
32356             }
32357             if (this.style) {
32358                 cfg.style = this.style;
32359             }
32360             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
32361             
32362             this.el.setVisibilityMode(Roo.Element.DISPLAY);
32363         }
32364         
32365         this.el.select('>button.close').on('click', this.hide, this);
32366         
32367     },
32368     
32369     show : function()
32370     {
32371         if (!this.rendered) {
32372             this.render();
32373         }
32374         
32375         this.el.show();
32376         
32377         this.fireEvent('show', this);
32378         
32379     },
32380     
32381     hide : function()
32382     {
32383         if (!this.rendered) {
32384             this.render();
32385         }
32386         
32387         this.el.hide();
32388         
32389         this.fireEvent('hide', this);
32390     },
32391     
32392     update : function()
32393     {
32394 //        var e = this.el.dom.firstChild;
32395 //        
32396 //        if(this.closable){
32397 //            e = e.nextSibling;
32398 //        }
32399 //        
32400 //        e.data = this.html || '';
32401
32402         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
32403     }
32404    
32405 });
32406
32407  
32408
32409      /*
32410  * - LGPL
32411  *
32412  * Graph
32413  * 
32414  */
32415
32416
32417 /**
32418  * @class Roo.bootstrap.Graph
32419  * @extends Roo.bootstrap.Component
32420  * Bootstrap Graph class
32421 > Prameters
32422  -sm {number} sm 4
32423  -md {number} md 5
32424  @cfg {String} graphtype  bar | vbar | pie
32425  @cfg {number} g_x coodinator | centre x (pie)
32426  @cfg {number} g_y coodinator | centre y (pie)
32427  @cfg {number} g_r radius (pie)
32428  @cfg {number} g_height height of the chart (respected by all elements in the set)
32429  @cfg {number} g_width width of the chart (respected by all elements in the set)
32430  @cfg {Object} title The title of the chart
32431     
32432  -{Array}  values
32433  -opts (object) options for the chart 
32434      o {
32435      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
32436      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
32437      o vgutter (number)
32438      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.
32439      o stacked (boolean) whether or not to tread values as in a stacked bar chart
32440      o to
32441      o stretch (boolean)
32442      o }
32443  -opts (object) options for the pie
32444      o{
32445      o cut
32446      o startAngle (number)
32447      o endAngle (number)
32448      } 
32449  *
32450  * @constructor
32451  * Create a new Input
32452  * @param {Object} config The config object
32453  */
32454
32455 Roo.bootstrap.Graph = function(config){
32456     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
32457     
32458     this.addEvents({
32459         // img events
32460         /**
32461          * @event click
32462          * The img click event for the img.
32463          * @param {Roo.EventObject} e
32464          */
32465         "click" : true
32466     });
32467 };
32468
32469 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
32470     
32471     sm: 4,
32472     md: 5,
32473     graphtype: 'bar',
32474     g_height: 250,
32475     g_width: 400,
32476     g_x: 50,
32477     g_y: 50,
32478     g_r: 30,
32479     opts:{
32480         //g_colors: this.colors,
32481         g_type: 'soft',
32482         g_gutter: '20%'
32483
32484     },
32485     title : false,
32486
32487     getAutoCreate : function(){
32488         
32489         var cfg = {
32490             tag: 'div',
32491             html : null
32492         };
32493         
32494         
32495         return  cfg;
32496     },
32497
32498     onRender : function(ct,position){
32499         
32500         
32501         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
32502         
32503         if (typeof(Raphael) == 'undefined') {
32504             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
32505             return;
32506         }
32507         
32508         this.raphael = Raphael(this.el.dom);
32509         
32510                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
32511                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
32512                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
32513                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
32514                 /*
32515                 r.text(160, 10, "Single Series Chart").attr(txtattr);
32516                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
32517                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
32518                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
32519                 
32520                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
32521                 r.barchart(330, 10, 300, 220, data1);
32522                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32523                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32524                 */
32525                 
32526                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32527                 // r.barchart(30, 30, 560, 250,  xdata, {
32528                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32529                 //     axis : "0 0 1 1",
32530                 //     axisxlabels :  xdata
32531                 //     //yvalues : cols,
32532                    
32533                 // });
32534 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32535 //        
32536 //        this.load(null,xdata,{
32537 //                axis : "0 0 1 1",
32538 //                axisxlabels :  xdata
32539 //                });
32540
32541     },
32542
32543     load : function(graphtype,xdata,opts)
32544     {
32545         this.raphael.clear();
32546         if(!graphtype) {
32547             graphtype = this.graphtype;
32548         }
32549         if(!opts){
32550             opts = this.opts;
32551         }
32552         var r = this.raphael,
32553             fin = function () {
32554                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32555             },
32556             fout = function () {
32557                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32558             },
32559             pfin = function() {
32560                 this.sector.stop();
32561                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32562
32563                 if (this.label) {
32564                     this.label[0].stop();
32565                     this.label[0].attr({ r: 7.5 });
32566                     this.label[1].attr({ "font-weight": 800 });
32567                 }
32568             },
32569             pfout = function() {
32570                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32571
32572                 if (this.label) {
32573                     this.label[0].animate({ r: 5 }, 500, "bounce");
32574                     this.label[1].attr({ "font-weight": 400 });
32575                 }
32576             };
32577
32578         switch(graphtype){
32579             case 'bar':
32580                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32581                 break;
32582             case 'hbar':
32583                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32584                 break;
32585             case 'pie':
32586 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
32587 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32588 //            
32589                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32590                 
32591                 break;
32592
32593         }
32594         
32595         if(this.title){
32596             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32597         }
32598         
32599     },
32600     
32601     setTitle: function(o)
32602     {
32603         this.title = o;
32604     },
32605     
32606     initEvents: function() {
32607         
32608         if(!this.href){
32609             this.el.on('click', this.onClick, this);
32610         }
32611     },
32612     
32613     onClick : function(e)
32614     {
32615         Roo.log('img onclick');
32616         this.fireEvent('click', this, e);
32617     }
32618    
32619 });
32620
32621  
32622 Roo.bootstrap.dash = {};/*
32623  * - LGPL
32624  *
32625  * numberBox
32626  * 
32627  */
32628 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32629
32630 /**
32631  * @class Roo.bootstrap.dash.NumberBox
32632  * @extends Roo.bootstrap.Component
32633  * Bootstrap NumberBox class
32634  * @cfg {String} headline Box headline
32635  * @cfg {String} content Box content
32636  * @cfg {String} icon Box icon
32637  * @cfg {String} footer Footer text
32638  * @cfg {String} fhref Footer href
32639  * 
32640  * @constructor
32641  * Create a new NumberBox
32642  * @param {Object} config The config object
32643  */
32644
32645
32646 Roo.bootstrap.dash.NumberBox = function(config){
32647     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32648     
32649 };
32650
32651 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
32652     
32653     headline : '',
32654     content : '',
32655     icon : '',
32656     footer : '',
32657     fhref : '',
32658     ficon : '',
32659     
32660     getAutoCreate : function(){
32661         
32662         var cfg = {
32663             tag : 'div',
32664             cls : 'small-box ',
32665             cn : [
32666                 {
32667                     tag : 'div',
32668                     cls : 'inner',
32669                     cn :[
32670                         {
32671                             tag : 'h3',
32672                             cls : 'roo-headline',
32673                             html : this.headline
32674                         },
32675                         {
32676                             tag : 'p',
32677                             cls : 'roo-content',
32678                             html : this.content
32679                         }
32680                     ]
32681                 }
32682             ]
32683         };
32684         
32685         if(this.icon){
32686             cfg.cn.push({
32687                 tag : 'div',
32688                 cls : 'icon',
32689                 cn :[
32690                     {
32691                         tag : 'i',
32692                         cls : 'ion ' + this.icon
32693                     }
32694                 ]
32695             });
32696         }
32697         
32698         if(this.footer){
32699             var footer = {
32700                 tag : 'a',
32701                 cls : 'small-box-footer',
32702                 href : this.fhref || '#',
32703                 html : this.footer
32704             };
32705             
32706             cfg.cn.push(footer);
32707             
32708         }
32709         
32710         return  cfg;
32711     },
32712
32713     onRender : function(ct,position){
32714         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32715
32716
32717        
32718                 
32719     },
32720
32721     setHeadline: function (value)
32722     {
32723         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32724     },
32725     
32726     setFooter: function (value, href)
32727     {
32728         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32729         
32730         if(href){
32731             this.el.select('a.small-box-footer',true).first().attr('href', href);
32732         }
32733         
32734     },
32735
32736     setContent: function (value)
32737     {
32738         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32739     },
32740
32741     initEvents: function() 
32742     {   
32743         
32744     }
32745     
32746 });
32747
32748  
32749 /*
32750  * - LGPL
32751  *
32752  * TabBox
32753  * 
32754  */
32755 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32756
32757 /**
32758  * @class Roo.bootstrap.dash.TabBox
32759  * @extends Roo.bootstrap.Component
32760  * @children Roo.bootstrap.dash.TabPane
32761  * Bootstrap TabBox class
32762  * @cfg {String} title Title of the TabBox
32763  * @cfg {String} icon Icon of the TabBox
32764  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32765  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32766  * 
32767  * @constructor
32768  * Create a new TabBox
32769  * @param {Object} config The config object
32770  */
32771
32772
32773 Roo.bootstrap.dash.TabBox = function(config){
32774     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32775     this.addEvents({
32776         // raw events
32777         /**
32778          * @event addpane
32779          * When a pane is added
32780          * @param {Roo.bootstrap.dash.TabPane} pane
32781          */
32782         "addpane" : true,
32783         /**
32784          * @event activatepane
32785          * When a pane is activated
32786          * @param {Roo.bootstrap.dash.TabPane} pane
32787          */
32788         "activatepane" : true
32789         
32790          
32791     });
32792     
32793     this.panes = [];
32794 };
32795
32796 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32797
32798     title : '',
32799     icon : false,
32800     showtabs : true,
32801     tabScrollable : false,
32802     
32803     getChildContainer : function()
32804     {
32805         return this.el.select('.tab-content', true).first();
32806     },
32807     
32808     getAutoCreate : function(){
32809         
32810         var header = {
32811             tag: 'li',
32812             cls: 'pull-left header',
32813             html: this.title,
32814             cn : []
32815         };
32816         
32817         if(this.icon){
32818             header.cn.push({
32819                 tag: 'i',
32820                 cls: 'fa ' + this.icon
32821             });
32822         }
32823         
32824         var h = {
32825             tag: 'ul',
32826             cls: 'nav nav-tabs pull-right',
32827             cn: [
32828                 header
32829             ]
32830         };
32831         
32832         if(this.tabScrollable){
32833             h = {
32834                 tag: 'div',
32835                 cls: 'tab-header',
32836                 cn: [
32837                     {
32838                         tag: 'ul',
32839                         cls: 'nav nav-tabs pull-right',
32840                         cn: [
32841                             header
32842                         ]
32843                     }
32844                 ]
32845             };
32846         }
32847         
32848         var cfg = {
32849             tag: 'div',
32850             cls: 'nav-tabs-custom',
32851             cn: [
32852                 h,
32853                 {
32854                     tag: 'div',
32855                     cls: 'tab-content no-padding',
32856                     cn: []
32857                 }
32858             ]
32859         };
32860
32861         return  cfg;
32862     },
32863     initEvents : function()
32864     {
32865         //Roo.log('add add pane handler');
32866         this.on('addpane', this.onAddPane, this);
32867     },
32868      /**
32869      * Updates the box title
32870      * @param {String} html to set the title to.
32871      */
32872     setTitle : function(value)
32873     {
32874         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32875     },
32876     onAddPane : function(pane)
32877     {
32878         this.panes.push(pane);
32879         //Roo.log('addpane');
32880         //Roo.log(pane);
32881         // tabs are rendere left to right..
32882         if(!this.showtabs){
32883             return;
32884         }
32885         
32886         var ctr = this.el.select('.nav-tabs', true).first();
32887          
32888          
32889         var existing = ctr.select('.nav-tab',true);
32890         var qty = existing.getCount();;
32891         
32892         
32893         var tab = ctr.createChild({
32894             tag : 'li',
32895             cls : 'nav-tab' + (qty ? '' : ' active'),
32896             cn : [
32897                 {
32898                     tag : 'a',
32899                     href:'#',
32900                     html : pane.title
32901                 }
32902             ]
32903         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32904         pane.tab = tab;
32905         
32906         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32907         if (!qty) {
32908             pane.el.addClass('active');
32909         }
32910         
32911                 
32912     },
32913     onTabClick : function(ev,un,ob,pane)
32914     {
32915         //Roo.log('tab - prev default');
32916         ev.preventDefault();
32917         
32918         
32919         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32920         pane.tab.addClass('active');
32921         //Roo.log(pane.title);
32922         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32923         // technically we should have a deactivate event.. but maybe add later.
32924         // and it should not de-activate the selected tab...
32925         this.fireEvent('activatepane', pane);
32926         pane.el.addClass('active');
32927         pane.fireEvent('activate');
32928         
32929         
32930     },
32931     
32932     getActivePane : function()
32933     {
32934         var r = false;
32935         Roo.each(this.panes, function(p) {
32936             if(p.el.hasClass('active')){
32937                 r = p;
32938                 return false;
32939             }
32940             
32941             return;
32942         });
32943         
32944         return r;
32945     }
32946     
32947     
32948 });
32949
32950  
32951 /*
32952  * - LGPL
32953  *
32954  * Tab pane
32955  * 
32956  */
32957 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32958 /**
32959  * @class Roo.bootstrap.TabPane
32960  * @extends Roo.bootstrap.Component
32961  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32962  * Bootstrap TabPane class
32963  * @cfg {Boolean} active (false | true) Default false
32964  * @cfg {String} title title of panel
32965
32966  * 
32967  * @constructor
32968  * Create a new TabPane
32969  * @param {Object} config The config object
32970  */
32971
32972 Roo.bootstrap.dash.TabPane = function(config){
32973     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32974     
32975     this.addEvents({
32976         // raw events
32977         /**
32978          * @event activate
32979          * When a pane is activated
32980          * @param {Roo.bootstrap.dash.TabPane} pane
32981          */
32982         "activate" : true
32983          
32984     });
32985 };
32986
32987 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32988     
32989     active : false,
32990     title : '',
32991     
32992     // the tabBox that this is attached to.
32993     tab : false,
32994      
32995     getAutoCreate : function() 
32996     {
32997         var cfg = {
32998             tag: 'div',
32999             cls: 'tab-pane'
33000         };
33001         
33002         if(this.active){
33003             cfg.cls += ' active';
33004         }
33005         
33006         return cfg;
33007     },
33008     initEvents  : function()
33009     {
33010         //Roo.log('trigger add pane handler');
33011         this.parent().fireEvent('addpane', this)
33012     },
33013     
33014      /**
33015      * Updates the tab title 
33016      * @param {String} html to set the title to.
33017      */
33018     setTitle: function(str)
33019     {
33020         if (!this.tab) {
33021             return;
33022         }
33023         this.title = str;
33024         this.tab.select('a', true).first().dom.innerHTML = str;
33025         
33026     }
33027     
33028     
33029     
33030 });
33031
33032  
33033
33034
33035  /*
33036  * - LGPL
33037  *
33038  * Tooltip
33039  * 
33040  */
33041
33042 /**
33043  * @class Roo.bootstrap.Tooltip
33044  * Bootstrap Tooltip class
33045  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33046  * to determine which dom element triggers the tooltip.
33047  * 
33048  * It needs to add support for additional attributes like tooltip-position
33049  * 
33050  * @constructor
33051  * Create a new Toolti
33052  * @param {Object} config The config object
33053  */
33054
33055 Roo.bootstrap.Tooltip = function(config){
33056     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33057     
33058     this.alignment = Roo.bootstrap.Tooltip.alignment;
33059     
33060     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33061         this.alignment = config.alignment;
33062     }
33063     
33064 };
33065
33066 Roo.apply(Roo.bootstrap.Tooltip, {
33067     /**
33068      * @function init initialize tooltip monitoring.
33069      * @static
33070      */
33071     currentEl : false,
33072     currentTip : false,
33073     currentRegion : false,
33074     
33075     //  init : delay?
33076     
33077     init : function()
33078     {
33079         Roo.get(document).on('mouseover', this.enter ,this);
33080         Roo.get(document).on('mouseout', this.leave, this);
33081          
33082         
33083         this.currentTip = new Roo.bootstrap.Tooltip();
33084     },
33085     
33086     enter : function(ev)
33087     {
33088         var dom = ev.getTarget();
33089         
33090         //Roo.log(['enter',dom]);
33091         var el = Roo.fly(dom);
33092         if (this.currentEl) {
33093             //Roo.log(dom);
33094             //Roo.log(this.currentEl);
33095             //Roo.log(this.currentEl.contains(dom));
33096             if (this.currentEl == el) {
33097                 return;
33098             }
33099             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33100                 return;
33101             }
33102
33103         }
33104         
33105         if (this.currentTip.el) {
33106             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33107         }    
33108         //Roo.log(ev);
33109         
33110         if(!el || el.dom == document){
33111             return;
33112         }
33113         
33114         var bindEl = el; 
33115         var pel = false;
33116         if (!el.attr('tooltip')) {
33117             pel = el.findParent("[tooltip]");
33118             if (pel) {
33119                 bindEl = Roo.get(pel);
33120             }
33121         }
33122         
33123        
33124         
33125         // you can not look for children, as if el is the body.. then everythign is the child..
33126         if (!pel && !el.attr('tooltip')) { //
33127             if (!el.select("[tooltip]").elements.length) {
33128                 return;
33129             }
33130             // is the mouse over this child...?
33131             bindEl = el.select("[tooltip]").first();
33132             var xy = ev.getXY();
33133             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33134                 //Roo.log("not in region.");
33135                 return;
33136             }
33137             //Roo.log("child element over..");
33138             
33139         }
33140         this.currentEl = el;
33141         this.currentTip.bind(bindEl);
33142         this.currentRegion = Roo.lib.Region.getRegion(dom);
33143         this.currentTip.enter();
33144         
33145     },
33146     leave : function(ev)
33147     {
33148         var dom = ev.getTarget();
33149         //Roo.log(['leave',dom]);
33150         if (!this.currentEl) {
33151             return;
33152         }
33153         
33154         
33155         if (dom != this.currentEl.dom) {
33156             return;
33157         }
33158         var xy = ev.getXY();
33159         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33160             return;
33161         }
33162         // only activate leave if mouse cursor is outside... bounding box..
33163         
33164         
33165         
33166         
33167         if (this.currentTip) {
33168             this.currentTip.leave();
33169         }
33170         //Roo.log('clear currentEl');
33171         this.currentEl = false;
33172         
33173         
33174     },
33175     alignment : {
33176         'left' : ['r-l', [-2,0], 'right'],
33177         'right' : ['l-r', [2,0], 'left'],
33178         'bottom' : ['t-b', [0,2], 'top'],
33179         'top' : [ 'b-t', [0,-2], 'bottom']
33180     }
33181     
33182 });
33183
33184
33185 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33186     
33187     
33188     bindEl : false,
33189     
33190     delay : null, // can be { show : 300 , hide: 500}
33191     
33192     timeout : null,
33193     
33194     hoverState : null, //???
33195     
33196     placement : 'bottom', 
33197     
33198     alignment : false,
33199     
33200     getAutoCreate : function(){
33201     
33202         var cfg = {
33203            cls : 'tooltip',   
33204            role : 'tooltip',
33205            cn : [
33206                 {
33207                     cls : 'tooltip-arrow arrow'
33208                 },
33209                 {
33210                     cls : 'tooltip-inner'
33211                 }
33212            ]
33213         };
33214         
33215         return cfg;
33216     },
33217     bind : function(el)
33218     {
33219         this.bindEl = el;
33220     },
33221     
33222     initEvents : function()
33223     {
33224         this.arrowEl = this.el.select('.arrow', true).first();
33225         this.innerEl = this.el.select('.tooltip-inner', true).first();
33226     },
33227     
33228     enter : function () {
33229        
33230         if (this.timeout != null) {
33231             clearTimeout(this.timeout);
33232         }
33233         
33234         this.hoverState = 'in';
33235          //Roo.log("enter - show");
33236         if (!this.delay || !this.delay.show) {
33237             this.show();
33238             return;
33239         }
33240         var _t = this;
33241         this.timeout = setTimeout(function () {
33242             if (_t.hoverState == 'in') {
33243                 _t.show();
33244             }
33245         }, this.delay.show);
33246     },
33247     leave : function()
33248     {
33249         clearTimeout(this.timeout);
33250     
33251         this.hoverState = 'out';
33252          if (!this.delay || !this.delay.hide) {
33253             this.hide();
33254             return;
33255         }
33256        
33257         var _t = this;
33258         this.timeout = setTimeout(function () {
33259             //Roo.log("leave - timeout");
33260             
33261             if (_t.hoverState == 'out') {
33262                 _t.hide();
33263                 Roo.bootstrap.Tooltip.currentEl = false;
33264             }
33265         }, delay);
33266     },
33267     
33268     show : function (msg)
33269     {
33270         if (!this.el) {
33271             this.render(document.body);
33272         }
33273         // set content.
33274         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
33275         
33276         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
33277         
33278         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
33279         
33280         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
33281                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
33282
33283         if(this.bindEl.attr('tooltip-class')) {
33284             this.el.addClass(this.bindEl.attr('tooltip-class'));
33285         }
33286         
33287         var placement = typeof this.placement == 'function' ?
33288             this.placement.call(this, this.el, on_el) :
33289             this.placement;
33290         
33291         if(this.bindEl.attr('tooltip-placement')) {
33292             placement = this.bindEl.attr('tooltip-placement');
33293         }
33294             
33295         var autoToken = /\s?auto?\s?/i;
33296         var autoPlace = autoToken.test(placement);
33297         if (autoPlace) {
33298             placement = placement.replace(autoToken, '') || 'top';
33299         }
33300         
33301         //this.el.detach()
33302         //this.el.setXY([0,0]);
33303         this.el.show();
33304         //this.el.dom.style.display='block';
33305         
33306         //this.el.appendTo(on_el);
33307         
33308         var p = this.getPosition();
33309         var box = this.el.getBox();
33310         
33311         if (autoPlace) {
33312             // fixme..
33313         }
33314         
33315         var align = this.alignment[placement];
33316         
33317         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
33318         
33319         if(placement == 'top' || placement == 'bottom'){
33320             if(xy[0] < 0){
33321                 placement = 'right';
33322             }
33323             
33324             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
33325                 placement = 'left';
33326             }
33327             
33328             var scroll = Roo.select('body', true).first().getScroll();
33329             
33330             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
33331                 placement = 'top';
33332             }
33333             
33334             align = this.alignment[placement];
33335             
33336             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
33337             
33338         }
33339         
33340         var elems = document.getElementsByTagName('div');
33341         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
33342         for (var i = 0; i < elems.length; i++) {
33343           var zindex = Number.parseInt(
33344                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
33345                 10
33346           );
33347           if (zindex > highest) {
33348             highest = zindex;
33349           }
33350         }
33351         
33352         
33353         
33354         this.el.dom.style.zIndex = highest;
33355         
33356         this.el.alignTo(this.bindEl, align[0],align[1]);
33357         //var arrow = this.el.select('.arrow',true).first();
33358         //arrow.set(align[2], 
33359         
33360         this.el.addClass(placement);
33361         this.el.addClass("bs-tooltip-"+ placement);
33362         
33363         this.el.addClass('in fade show');
33364         
33365         this.hoverState = null;
33366         
33367         if (this.el.hasClass('fade')) {
33368             // fade it?
33369         }
33370         
33371         
33372         
33373         
33374         
33375     },
33376     hide : function()
33377     {
33378          
33379         if (!this.el) {
33380             return;
33381         }
33382         //this.el.setXY([0,0]);
33383         if(this.bindEl.attr('tooltip-class')) {
33384             this.el.removeClass(this.bindEl.attr('tooltip-class'));
33385         }
33386         this.el.removeClass(['show', 'in']);
33387         //this.el.hide();
33388         
33389     }
33390     
33391 });
33392  
33393
33394  /*
33395  * - LGPL
33396  *
33397  * Location Picker
33398  * 
33399  */
33400
33401 /**
33402  * @class Roo.bootstrap.LocationPicker
33403  * @extends Roo.bootstrap.Component
33404  * Bootstrap LocationPicker class
33405  * @cfg {Number} latitude Position when init default 0
33406  * @cfg {Number} longitude Position when init default 0
33407  * @cfg {Number} zoom default 15
33408  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
33409  * @cfg {Boolean} mapTypeControl default false
33410  * @cfg {Boolean} disableDoubleClickZoom default false
33411  * @cfg {Boolean} scrollwheel default true
33412  * @cfg {Boolean} streetViewControl default false
33413  * @cfg {Number} radius default 0
33414  * @cfg {String} locationName
33415  * @cfg {Boolean} draggable default true
33416  * @cfg {Boolean} enableAutocomplete default false
33417  * @cfg {Boolean} enableReverseGeocode default true
33418  * @cfg {String} markerTitle
33419  * 
33420  * @constructor
33421  * Create a new LocationPicker
33422  * @param {Object} config The config object
33423  */
33424
33425
33426 Roo.bootstrap.LocationPicker = function(config){
33427     
33428     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
33429     
33430     this.addEvents({
33431         /**
33432          * @event initial
33433          * Fires when the picker initialized.
33434          * @param {Roo.bootstrap.LocationPicker} this
33435          * @param {Google Location} location
33436          */
33437         initial : true,
33438         /**
33439          * @event positionchanged
33440          * Fires when the picker position changed.
33441          * @param {Roo.bootstrap.LocationPicker} this
33442          * @param {Google Location} location
33443          */
33444         positionchanged : true,
33445         /**
33446          * @event resize
33447          * Fires when the map resize.
33448          * @param {Roo.bootstrap.LocationPicker} this
33449          */
33450         resize : true,
33451         /**
33452          * @event show
33453          * Fires when the map show.
33454          * @param {Roo.bootstrap.LocationPicker} this
33455          */
33456         show : true,
33457         /**
33458          * @event hide
33459          * Fires when the map hide.
33460          * @param {Roo.bootstrap.LocationPicker} this
33461          */
33462         hide : true,
33463         /**
33464          * @event mapClick
33465          * Fires when click the map.
33466          * @param {Roo.bootstrap.LocationPicker} this
33467          * @param {Map event} e
33468          */
33469         mapClick : true,
33470         /**
33471          * @event mapRightClick
33472          * Fires when right click the map.
33473          * @param {Roo.bootstrap.LocationPicker} this
33474          * @param {Map event} e
33475          */
33476         mapRightClick : true,
33477         /**
33478          * @event markerClick
33479          * Fires when click the marker.
33480          * @param {Roo.bootstrap.LocationPicker} this
33481          * @param {Map event} e
33482          */
33483         markerClick : true,
33484         /**
33485          * @event markerRightClick
33486          * Fires when right click the marker.
33487          * @param {Roo.bootstrap.LocationPicker} this
33488          * @param {Map event} e
33489          */
33490         markerRightClick : true,
33491         /**
33492          * @event OverlayViewDraw
33493          * Fires when OverlayView Draw
33494          * @param {Roo.bootstrap.LocationPicker} this
33495          */
33496         OverlayViewDraw : true,
33497         /**
33498          * @event OverlayViewOnAdd
33499          * Fires when OverlayView Draw
33500          * @param {Roo.bootstrap.LocationPicker} this
33501          */
33502         OverlayViewOnAdd : true,
33503         /**
33504          * @event OverlayViewOnRemove
33505          * Fires when OverlayView Draw
33506          * @param {Roo.bootstrap.LocationPicker} this
33507          */
33508         OverlayViewOnRemove : true,
33509         /**
33510          * @event OverlayViewShow
33511          * Fires when OverlayView Draw
33512          * @param {Roo.bootstrap.LocationPicker} this
33513          * @param {Pixel} cpx
33514          */
33515         OverlayViewShow : true,
33516         /**
33517          * @event OverlayViewHide
33518          * Fires when OverlayView Draw
33519          * @param {Roo.bootstrap.LocationPicker} this
33520          */
33521         OverlayViewHide : true,
33522         /**
33523          * @event loadexception
33524          * Fires when load google lib failed.
33525          * @param {Roo.bootstrap.LocationPicker} this
33526          */
33527         loadexception : true
33528     });
33529         
33530 };
33531
33532 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33533     
33534     gMapContext: false,
33535     
33536     latitude: 0,
33537     longitude: 0,
33538     zoom: 15,
33539     mapTypeId: false,
33540     mapTypeControl: false,
33541     disableDoubleClickZoom: false,
33542     scrollwheel: true,
33543     streetViewControl: false,
33544     radius: 0,
33545     locationName: '',
33546     draggable: true,
33547     enableAutocomplete: false,
33548     enableReverseGeocode: true,
33549     markerTitle: '',
33550     
33551     getAutoCreate: function()
33552     {
33553
33554         var cfg = {
33555             tag: 'div',
33556             cls: 'roo-location-picker'
33557         };
33558         
33559         return cfg
33560     },
33561     
33562     initEvents: function(ct, position)
33563     {       
33564         if(!this.el.getWidth() || this.isApplied()){
33565             return;
33566         }
33567         
33568         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33569         
33570         this.initial();
33571     },
33572     
33573     initial: function()
33574     {
33575         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33576             this.fireEvent('loadexception', this);
33577             return;
33578         }
33579         
33580         if(!this.mapTypeId){
33581             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33582         }
33583         
33584         this.gMapContext = this.GMapContext();
33585         
33586         this.initOverlayView();
33587         
33588         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33589         
33590         var _this = this;
33591                 
33592         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33593             _this.setPosition(_this.gMapContext.marker.position);
33594         });
33595         
33596         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33597             _this.fireEvent('mapClick', this, event);
33598             
33599         });
33600
33601         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33602             _this.fireEvent('mapRightClick', this, event);
33603             
33604         });
33605         
33606         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33607             _this.fireEvent('markerClick', this, event);
33608             
33609         });
33610
33611         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33612             _this.fireEvent('markerRightClick', this, event);
33613             
33614         });
33615         
33616         this.setPosition(this.gMapContext.location);
33617         
33618         this.fireEvent('initial', this, this.gMapContext.location);
33619     },
33620     
33621     initOverlayView: function()
33622     {
33623         var _this = this;
33624         
33625         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33626             
33627             draw: function()
33628             {
33629                 _this.fireEvent('OverlayViewDraw', _this);
33630             },
33631             
33632             onAdd: function()
33633             {
33634                 _this.fireEvent('OverlayViewOnAdd', _this);
33635             },
33636             
33637             onRemove: function()
33638             {
33639                 _this.fireEvent('OverlayViewOnRemove', _this);
33640             },
33641             
33642             show: function(cpx)
33643             {
33644                 _this.fireEvent('OverlayViewShow', _this, cpx);
33645             },
33646             
33647             hide: function()
33648             {
33649                 _this.fireEvent('OverlayViewHide', _this);
33650             }
33651             
33652         });
33653     },
33654     
33655     fromLatLngToContainerPixel: function(event)
33656     {
33657         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33658     },
33659     
33660     isApplied: function() 
33661     {
33662         return this.getGmapContext() == false ? false : true;
33663     },
33664     
33665     getGmapContext: function() 
33666     {
33667         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33668     },
33669     
33670     GMapContext: function() 
33671     {
33672         var position = new google.maps.LatLng(this.latitude, this.longitude);
33673         
33674         var _map = new google.maps.Map(this.el.dom, {
33675             center: position,
33676             zoom: this.zoom,
33677             mapTypeId: this.mapTypeId,
33678             mapTypeControl: this.mapTypeControl,
33679             disableDoubleClickZoom: this.disableDoubleClickZoom,
33680             scrollwheel: this.scrollwheel,
33681             streetViewControl: this.streetViewControl,
33682             locationName: this.locationName,
33683             draggable: this.draggable,
33684             enableAutocomplete: this.enableAutocomplete,
33685             enableReverseGeocode: this.enableReverseGeocode
33686         });
33687         
33688         var _marker = new google.maps.Marker({
33689             position: position,
33690             map: _map,
33691             title: this.markerTitle,
33692             draggable: this.draggable
33693         });
33694         
33695         return {
33696             map: _map,
33697             marker: _marker,
33698             circle: null,
33699             location: position,
33700             radius: this.radius,
33701             locationName: this.locationName,
33702             addressComponents: {
33703                 formatted_address: null,
33704                 addressLine1: null,
33705                 addressLine2: null,
33706                 streetName: null,
33707                 streetNumber: null,
33708                 city: null,
33709                 district: null,
33710                 state: null,
33711                 stateOrProvince: null
33712             },
33713             settings: this,
33714             domContainer: this.el.dom,
33715             geodecoder: new google.maps.Geocoder()
33716         };
33717     },
33718     
33719     drawCircle: function(center, radius, options) 
33720     {
33721         if (this.gMapContext.circle != null) {
33722             this.gMapContext.circle.setMap(null);
33723         }
33724         if (radius > 0) {
33725             radius *= 1;
33726             options = Roo.apply({}, options, {
33727                 strokeColor: "#0000FF",
33728                 strokeOpacity: .35,
33729                 strokeWeight: 2,
33730                 fillColor: "#0000FF",
33731                 fillOpacity: .2
33732             });
33733             
33734             options.map = this.gMapContext.map;
33735             options.radius = radius;
33736             options.center = center;
33737             this.gMapContext.circle = new google.maps.Circle(options);
33738             return this.gMapContext.circle;
33739         }
33740         
33741         return null;
33742     },
33743     
33744     setPosition: function(location) 
33745     {
33746         this.gMapContext.location = location;
33747         this.gMapContext.marker.setPosition(location);
33748         this.gMapContext.map.panTo(location);
33749         this.drawCircle(location, this.gMapContext.radius, {});
33750         
33751         var _this = this;
33752         
33753         if (this.gMapContext.settings.enableReverseGeocode) {
33754             this.gMapContext.geodecoder.geocode({
33755                 latLng: this.gMapContext.location
33756             }, function(results, status) {
33757                 
33758                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33759                     _this.gMapContext.locationName = results[0].formatted_address;
33760                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33761                     
33762                     _this.fireEvent('positionchanged', this, location);
33763                 }
33764             });
33765             
33766             return;
33767         }
33768         
33769         this.fireEvent('positionchanged', this, location);
33770     },
33771     
33772     resize: function()
33773     {
33774         google.maps.event.trigger(this.gMapContext.map, "resize");
33775         
33776         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33777         
33778         this.fireEvent('resize', this);
33779     },
33780     
33781     setPositionByLatLng: function(latitude, longitude)
33782     {
33783         this.setPosition(new google.maps.LatLng(latitude, longitude));
33784     },
33785     
33786     getCurrentPosition: function() 
33787     {
33788         return {
33789             latitude: this.gMapContext.location.lat(),
33790             longitude: this.gMapContext.location.lng()
33791         };
33792     },
33793     
33794     getAddressName: function() 
33795     {
33796         return this.gMapContext.locationName;
33797     },
33798     
33799     getAddressComponents: function() 
33800     {
33801         return this.gMapContext.addressComponents;
33802     },
33803     
33804     address_component_from_google_geocode: function(address_components) 
33805     {
33806         var result = {};
33807         
33808         for (var i = 0; i < address_components.length; i++) {
33809             var component = address_components[i];
33810             if (component.types.indexOf("postal_code") >= 0) {
33811                 result.postalCode = component.short_name;
33812             } else if (component.types.indexOf("street_number") >= 0) {
33813                 result.streetNumber = component.short_name;
33814             } else if (component.types.indexOf("route") >= 0) {
33815                 result.streetName = component.short_name;
33816             } else if (component.types.indexOf("neighborhood") >= 0) {
33817                 result.city = component.short_name;
33818             } else if (component.types.indexOf("locality") >= 0) {
33819                 result.city = component.short_name;
33820             } else if (component.types.indexOf("sublocality") >= 0) {
33821                 result.district = component.short_name;
33822             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33823                 result.stateOrProvince = component.short_name;
33824             } else if (component.types.indexOf("country") >= 0) {
33825                 result.country = component.short_name;
33826             }
33827         }
33828         
33829         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33830         result.addressLine2 = "";
33831         return result;
33832     },
33833     
33834     setZoomLevel: function(zoom)
33835     {
33836         this.gMapContext.map.setZoom(zoom);
33837     },
33838     
33839     show: function()
33840     {
33841         if(!this.el){
33842             return;
33843         }
33844         
33845         this.el.show();
33846         
33847         this.resize();
33848         
33849         this.fireEvent('show', this);
33850     },
33851     
33852     hide: function()
33853     {
33854         if(!this.el){
33855             return;
33856         }
33857         
33858         this.el.hide();
33859         
33860         this.fireEvent('hide', this);
33861     }
33862     
33863 });
33864
33865 Roo.apply(Roo.bootstrap.LocationPicker, {
33866     
33867     OverlayView : function(map, options)
33868     {
33869         options = options || {};
33870         
33871         this.setMap(map);
33872     }
33873     
33874     
33875 });/**
33876  * @class Roo.bootstrap.Alert
33877  * @extends Roo.bootstrap.Component
33878  * Bootstrap Alert class - shows an alert area box
33879  * eg
33880  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33881   Enter a valid email address
33882 </div>
33883  * @licence LGPL
33884  * @cfg {String} title The title of alert
33885  * @cfg {String} html The content of alert
33886  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33887  * @cfg {String} fa font-awesomeicon
33888  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33889  * @cfg {Boolean} close true to show a x closer
33890  * 
33891  * 
33892  * @constructor
33893  * Create a new alert
33894  * @param {Object} config The config object
33895  */
33896
33897
33898 Roo.bootstrap.Alert = function(config){
33899     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33900     
33901 };
33902
33903 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33904     
33905     title: '',
33906     html: '',
33907     weight: false,
33908     fa: false,
33909     faicon: false, // BC
33910     close : false,
33911     
33912     
33913     getAutoCreate : function()
33914     {
33915         
33916         var cfg = {
33917             tag : 'div',
33918             cls : 'alert',
33919             cn : [
33920                 {
33921                     tag: 'button',
33922                     type :  "button",
33923                     cls: "close",
33924                     html : '×',
33925                     style : this.close ? '' : 'display:none'
33926                 },
33927                 {
33928                     tag : 'i',
33929                     cls : 'roo-alert-icon'
33930                     
33931                 },
33932                 {
33933                     tag : 'b',
33934                     cls : 'roo-alert-title',
33935                     html : this.title
33936                 },
33937                 {
33938                     tag : 'span',
33939                     cls : 'roo-alert-text',
33940                     html : this.html
33941                 }
33942             ]
33943         };
33944         
33945         if(this.faicon){
33946             cfg.cn[0].cls += ' fa ' + this.faicon;
33947         }
33948         if(this.fa){
33949             cfg.cn[0].cls += ' fa ' + this.fa;
33950         }
33951         
33952         if(this.weight){
33953             cfg.cls += ' alert-' + this.weight;
33954         }
33955         
33956         return cfg;
33957     },
33958     
33959     initEvents: function() 
33960     {
33961         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33962         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33963         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33964         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33965         if (this.seconds > 0) {
33966             this.hide.defer(this.seconds, this);
33967         }
33968     },
33969     /**
33970      * Set the Title Message HTML
33971      * @param {String} html
33972      */
33973     setTitle : function(str)
33974     {
33975         this.titleEl.dom.innerHTML = str;
33976     },
33977      
33978      /**
33979      * Set the Body Message HTML
33980      * @param {String} html
33981      */
33982     setHtml : function(str)
33983     {
33984         this.htmlEl.dom.innerHTML = str;
33985     },
33986     /**
33987      * Set the Weight of the alert
33988      * @param {String} (success|info|warning|danger) weight
33989      */
33990     
33991     setWeight : function(weight)
33992     {
33993         if(this.weight){
33994             this.el.removeClass('alert-' + this.weight);
33995         }
33996         
33997         this.weight = weight;
33998         
33999         this.el.addClass('alert-' + this.weight);
34000     },
34001       /**
34002      * Set the Icon of the alert
34003      * @param {String} see fontawsome names (name without the 'fa-' bit)
34004      */
34005     setIcon : function(icon)
34006     {
34007         if(this.faicon){
34008             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34009         }
34010         
34011         this.faicon = icon;
34012         
34013         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34014     },
34015     /**
34016      * Hide the Alert
34017      */
34018     hide: function() 
34019     {
34020         this.el.hide();   
34021     },
34022     /**
34023      * Show the Alert
34024      */
34025     show: function() 
34026     {  
34027         this.el.show();   
34028     }
34029     
34030 });
34031
34032  
34033 /*
34034 * Licence: LGPL
34035 */
34036
34037 /**
34038  * @class Roo.bootstrap.UploadCropbox
34039  * @extends Roo.bootstrap.Component
34040  * Bootstrap UploadCropbox class
34041  * @cfg {String} emptyText show when image has been loaded
34042  * @cfg {String} rotateNotify show when image too small to rotate
34043  * @cfg {Number} errorTimeout default 3000
34044  * @cfg {Number} minWidth default 300
34045  * @cfg {Number} minHeight default 300
34046  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34047  * @cfg {Boolean} isDocument (true|false) default false
34048  * @cfg {String} url action url
34049  * @cfg {String} paramName default 'imageUpload'
34050  * @cfg {String} method default POST
34051  * @cfg {Boolean} loadMask (true|false) default true
34052  * @cfg {Boolean} loadingText default 'Loading...'
34053  * 
34054  * @constructor
34055  * Create a new UploadCropbox
34056  * @param {Object} config The config object
34057  */
34058
34059 Roo.bootstrap.UploadCropbox = function(config){
34060     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34061     
34062     this.addEvents({
34063         /**
34064          * @event beforeselectfile
34065          * Fire before select file
34066          * @param {Roo.bootstrap.UploadCropbox} this
34067          */
34068         "beforeselectfile" : true,
34069         /**
34070          * @event initial
34071          * Fire after initEvent
34072          * @param {Roo.bootstrap.UploadCropbox} this
34073          */
34074         "initial" : true,
34075         /**
34076          * @event crop
34077          * Fire after initEvent
34078          * @param {Roo.bootstrap.UploadCropbox} this
34079          * @param {String} data
34080          */
34081         "crop" : true,
34082         /**
34083          * @event prepare
34084          * Fire when preparing the file data
34085          * @param {Roo.bootstrap.UploadCropbox} this
34086          * @param {Object} file
34087          */
34088         "prepare" : true,
34089         /**
34090          * @event exception
34091          * Fire when get exception
34092          * @param {Roo.bootstrap.UploadCropbox} this
34093          * @param {XMLHttpRequest} xhr
34094          */
34095         "exception" : true,
34096         /**
34097          * @event beforeloadcanvas
34098          * Fire before load the canvas
34099          * @param {Roo.bootstrap.UploadCropbox} this
34100          * @param {String} src
34101          */
34102         "beforeloadcanvas" : true,
34103         /**
34104          * @event trash
34105          * Fire when trash image
34106          * @param {Roo.bootstrap.UploadCropbox} this
34107          */
34108         "trash" : true,
34109         /**
34110          * @event download
34111          * Fire when download the image
34112          * @param {Roo.bootstrap.UploadCropbox} this
34113          */
34114         "download" : true,
34115         /**
34116          * @event footerbuttonclick
34117          * Fire when footerbuttonclick
34118          * @param {Roo.bootstrap.UploadCropbox} this
34119          * @param {String} type
34120          */
34121         "footerbuttonclick" : true,
34122         /**
34123          * @event resize
34124          * Fire when resize
34125          * @param {Roo.bootstrap.UploadCropbox} this
34126          */
34127         "resize" : true,
34128         /**
34129          * @event rotate
34130          * Fire when rotate the image
34131          * @param {Roo.bootstrap.UploadCropbox} this
34132          * @param {String} pos
34133          */
34134         "rotate" : true,
34135         /**
34136          * @event inspect
34137          * Fire when inspect the file
34138          * @param {Roo.bootstrap.UploadCropbox} this
34139          * @param {Object} file
34140          */
34141         "inspect" : true,
34142         /**
34143          * @event upload
34144          * Fire when xhr upload the file
34145          * @param {Roo.bootstrap.UploadCropbox} this
34146          * @param {Object} data
34147          */
34148         "upload" : true,
34149         /**
34150          * @event arrange
34151          * Fire when arrange the file data
34152          * @param {Roo.bootstrap.UploadCropbox} this
34153          * @param {Object} formData
34154          */
34155         "arrange" : true
34156     });
34157     
34158     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34159 };
34160
34161 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34162     
34163     emptyText : 'Click to upload image',
34164     rotateNotify : 'Image is too small to rotate',
34165     errorTimeout : 3000,
34166     scale : 0,
34167     baseScale : 1,
34168     rotate : 0,
34169     dragable : false,
34170     pinching : false,
34171     mouseX : 0,
34172     mouseY : 0,
34173     cropData : false,
34174     minWidth : 300,
34175     minHeight : 300,
34176     file : false,
34177     exif : {},
34178     baseRotate : 1,
34179     cropType : 'image/jpeg',
34180     buttons : false,
34181     canvasLoaded : false,
34182     isDocument : false,
34183     method : 'POST',
34184     paramName : 'imageUpload',
34185     loadMask : true,
34186     loadingText : 'Loading...',
34187     maskEl : false,
34188     
34189     getAutoCreate : function()
34190     {
34191         var cfg = {
34192             tag : 'div',
34193             cls : 'roo-upload-cropbox',
34194             cn : [
34195                 {
34196                     tag : 'input',
34197                     cls : 'roo-upload-cropbox-selector',
34198                     type : 'file'
34199                 },
34200                 {
34201                     tag : 'div',
34202                     cls : 'roo-upload-cropbox-body',
34203                     style : 'cursor:pointer',
34204                     cn : [
34205                         {
34206                             tag : 'div',
34207                             cls : 'roo-upload-cropbox-preview'
34208                         },
34209                         {
34210                             tag : 'div',
34211                             cls : 'roo-upload-cropbox-thumb'
34212                         },
34213                         {
34214                             tag : 'div',
34215                             cls : 'roo-upload-cropbox-empty-notify',
34216                             html : this.emptyText
34217                         },
34218                         {
34219                             tag : 'div',
34220                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
34221                             html : this.rotateNotify
34222                         }
34223                     ]
34224                 },
34225                 {
34226                     tag : 'div',
34227                     cls : 'roo-upload-cropbox-footer',
34228                     cn : {
34229                         tag : 'div',
34230                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
34231                         cn : []
34232                     }
34233                 }
34234             ]
34235         };
34236         
34237         return cfg;
34238     },
34239     
34240     onRender : function(ct, position)
34241     {
34242         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
34243         
34244         if (this.buttons.length) {
34245             
34246             Roo.each(this.buttons, function(bb) {
34247                 
34248                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
34249                 
34250                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
34251                 
34252             }, this);
34253         }
34254         
34255         if(this.loadMask){
34256             this.maskEl = this.el;
34257         }
34258     },
34259     
34260     initEvents : function()
34261     {
34262         this.urlAPI = (window.createObjectURL && window) || 
34263                                 (window.URL && URL.revokeObjectURL && URL) || 
34264                                 (window.webkitURL && webkitURL);
34265                         
34266         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
34267         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
34268         
34269         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
34270         this.selectorEl.hide();
34271         
34272         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
34273         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
34274         
34275         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
34276         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
34277         this.thumbEl.hide();
34278         
34279         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
34280         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
34281         
34282         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
34283         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
34284         this.errorEl.hide();
34285         
34286         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
34287         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
34288         this.footerEl.hide();
34289         
34290         this.setThumbBoxSize();
34291         
34292         this.bind();
34293         
34294         this.resize();
34295         
34296         this.fireEvent('initial', this);
34297     },
34298
34299     bind : function()
34300     {
34301         var _this = this;
34302         
34303         window.addEventListener("resize", function() { _this.resize(); } );
34304         
34305         this.bodyEl.on('click', this.beforeSelectFile, this);
34306         
34307         if(Roo.isTouch){
34308             this.bodyEl.on('touchstart', this.onTouchStart, this);
34309             this.bodyEl.on('touchmove', this.onTouchMove, this);
34310             this.bodyEl.on('touchend', this.onTouchEnd, this);
34311         }
34312         
34313         if(!Roo.isTouch){
34314             this.bodyEl.on('mousedown', this.onMouseDown, this);
34315             this.bodyEl.on('mousemove', this.onMouseMove, this);
34316             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
34317             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
34318             Roo.get(document).on('mouseup', this.onMouseUp, this);
34319         }
34320         
34321         this.selectorEl.on('change', this.onFileSelected, this);
34322     },
34323     
34324     reset : function()
34325     {    
34326         this.scale = 0;
34327         this.baseScale = 1;
34328         this.rotate = 0;
34329         this.baseRotate = 1;
34330         this.dragable = false;
34331         this.pinching = false;
34332         this.mouseX = 0;
34333         this.mouseY = 0;
34334         this.cropData = false;
34335         this.notifyEl.dom.innerHTML = this.emptyText;
34336         
34337         this.selectorEl.dom.value = '';
34338         
34339     },
34340     
34341     resize : function()
34342     {
34343         if(this.fireEvent('resize', this) != false){
34344             this.setThumbBoxPosition();
34345             this.setCanvasPosition();
34346         }
34347     },
34348     
34349     onFooterButtonClick : function(e, el, o, type)
34350     {
34351         switch (type) {
34352             case 'rotate-left' :
34353                 this.onRotateLeft(e);
34354                 break;
34355             case 'rotate-right' :
34356                 this.onRotateRight(e);
34357                 break;
34358             case 'picture' :
34359                 this.beforeSelectFile(e);
34360                 break;
34361             case 'trash' :
34362                 this.trash(e);
34363                 break;
34364             case 'crop' :
34365                 this.crop(e);
34366                 break;
34367             case 'download' :
34368                 this.download(e);
34369                 break;
34370             default :
34371                 break;
34372         }
34373         
34374         this.fireEvent('footerbuttonclick', this, type);
34375     },
34376     
34377     beforeSelectFile : function(e)
34378     {
34379         e.preventDefault();
34380         
34381         if(this.fireEvent('beforeselectfile', this) != false){
34382             this.selectorEl.dom.click();
34383         }
34384     },
34385     
34386     onFileSelected : function(e)
34387     {
34388         e.preventDefault();
34389         
34390         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
34391             return;
34392         }
34393         
34394         var file = this.selectorEl.dom.files[0];
34395         
34396         if(this.fireEvent('inspect', this, file) != false){
34397             this.prepare(file);
34398         }
34399         
34400     },
34401     
34402     trash : function(e)
34403     {
34404         this.fireEvent('trash', this);
34405     },
34406     
34407     download : function(e)
34408     {
34409         this.fireEvent('download', this);
34410     },
34411     
34412     loadCanvas : function(src)
34413     {   
34414         if(this.fireEvent('beforeloadcanvas', this, src) != false){
34415             
34416             this.reset();
34417             
34418             this.imageEl = document.createElement('img');
34419             
34420             var _this = this;
34421             
34422             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
34423             
34424             this.imageEl.src = src;
34425         }
34426     },
34427     
34428     onLoadCanvas : function()
34429     {   
34430         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
34431         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
34432         
34433         this.bodyEl.un('click', this.beforeSelectFile, this);
34434         
34435         this.notifyEl.hide();
34436         this.thumbEl.show();
34437         this.footerEl.show();
34438         
34439         this.baseRotateLevel();
34440         
34441         if(this.isDocument){
34442             this.setThumbBoxSize();
34443         }
34444         
34445         this.setThumbBoxPosition();
34446         
34447         this.baseScaleLevel();
34448         
34449         this.draw();
34450         
34451         this.resize();
34452         
34453         this.canvasLoaded = true;
34454         
34455         if(this.loadMask){
34456             this.maskEl.unmask();
34457         }
34458         
34459     },
34460     
34461     setCanvasPosition : function()
34462     {   
34463         if(!this.canvasEl){
34464             return;
34465         }
34466         
34467         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
34468         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
34469         
34470         this.previewEl.setLeft(pw);
34471         this.previewEl.setTop(ph);
34472         
34473     },
34474     
34475     onMouseDown : function(e)
34476     {   
34477         e.stopEvent();
34478         
34479         this.dragable = true;
34480         this.pinching = false;
34481         
34482         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
34483             this.dragable = false;
34484             return;
34485         }
34486         
34487         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34488         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34489         
34490     },
34491     
34492     onMouseMove : function(e)
34493     {   
34494         e.stopEvent();
34495         
34496         if(!this.canvasLoaded){
34497             return;
34498         }
34499         
34500         if (!this.dragable){
34501             return;
34502         }
34503         
34504         var minX = Math.ceil(this.thumbEl.getLeft(true));
34505         var minY = Math.ceil(this.thumbEl.getTop(true));
34506         
34507         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
34508         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
34509         
34510         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34511         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34512         
34513         x = x - this.mouseX;
34514         y = y - this.mouseY;
34515         
34516         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
34517         var bgY = Math.ceil(y + this.previewEl.getTop(true));
34518         
34519         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
34520         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
34521         
34522         this.previewEl.setLeft(bgX);
34523         this.previewEl.setTop(bgY);
34524         
34525         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34526         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34527     },
34528     
34529     onMouseUp : function(e)
34530     {   
34531         e.stopEvent();
34532         
34533         this.dragable = false;
34534     },
34535     
34536     onMouseWheel : function(e)
34537     {   
34538         e.stopEvent();
34539         
34540         this.startScale = this.scale;
34541         
34542         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34543         
34544         if(!this.zoomable()){
34545             this.scale = this.startScale;
34546             return;
34547         }
34548         
34549         this.draw();
34550         
34551         return;
34552     },
34553     
34554     zoomable : function()
34555     {
34556         var minScale = this.thumbEl.getWidth() / this.minWidth;
34557         
34558         if(this.minWidth < this.minHeight){
34559             minScale = this.thumbEl.getHeight() / this.minHeight;
34560         }
34561         
34562         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34563         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34564         
34565         if(
34566                 this.isDocument &&
34567                 (this.rotate == 0 || this.rotate == 180) && 
34568                 (
34569                     width > this.imageEl.OriginWidth || 
34570                     height > this.imageEl.OriginHeight ||
34571                     (width < this.minWidth && height < this.minHeight)
34572                 )
34573         ){
34574             return false;
34575         }
34576         
34577         if(
34578                 this.isDocument &&
34579                 (this.rotate == 90 || this.rotate == 270) && 
34580                 (
34581                     width > this.imageEl.OriginWidth || 
34582                     height > this.imageEl.OriginHeight ||
34583                     (width < this.minHeight && height < this.minWidth)
34584                 )
34585         ){
34586             return false;
34587         }
34588         
34589         if(
34590                 !this.isDocument &&
34591                 (this.rotate == 0 || this.rotate == 180) && 
34592                 (
34593                     width < this.minWidth || 
34594                     width > this.imageEl.OriginWidth || 
34595                     height < this.minHeight || 
34596                     height > this.imageEl.OriginHeight
34597                 )
34598         ){
34599             return false;
34600         }
34601         
34602         if(
34603                 !this.isDocument &&
34604                 (this.rotate == 90 || this.rotate == 270) && 
34605                 (
34606                     width < this.minHeight || 
34607                     width > this.imageEl.OriginWidth || 
34608                     height < this.minWidth || 
34609                     height > this.imageEl.OriginHeight
34610                 )
34611         ){
34612             return false;
34613         }
34614         
34615         return true;
34616         
34617     },
34618     
34619     onRotateLeft : function(e)
34620     {   
34621         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34622             
34623             var minScale = this.thumbEl.getWidth() / this.minWidth;
34624             
34625             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34626             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34627             
34628             this.startScale = this.scale;
34629             
34630             while (this.getScaleLevel() < minScale){
34631             
34632                 this.scale = this.scale + 1;
34633                 
34634                 if(!this.zoomable()){
34635                     break;
34636                 }
34637                 
34638                 if(
34639                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34640                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34641                 ){
34642                     continue;
34643                 }
34644                 
34645                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34646
34647                 this.draw();
34648                 
34649                 return;
34650             }
34651             
34652             this.scale = this.startScale;
34653             
34654             this.onRotateFail();
34655             
34656             return false;
34657         }
34658         
34659         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34660
34661         if(this.isDocument){
34662             this.setThumbBoxSize();
34663             this.setThumbBoxPosition();
34664             this.setCanvasPosition();
34665         }
34666         
34667         this.draw();
34668         
34669         this.fireEvent('rotate', this, 'left');
34670         
34671     },
34672     
34673     onRotateRight : function(e)
34674     {
34675         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34676             
34677             var minScale = this.thumbEl.getWidth() / this.minWidth;
34678         
34679             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34680             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34681             
34682             this.startScale = this.scale;
34683             
34684             while (this.getScaleLevel() < minScale){
34685             
34686                 this.scale = this.scale + 1;
34687                 
34688                 if(!this.zoomable()){
34689                     break;
34690                 }
34691                 
34692                 if(
34693                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34694                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34695                 ){
34696                     continue;
34697                 }
34698                 
34699                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34700
34701                 this.draw();
34702                 
34703                 return;
34704             }
34705             
34706             this.scale = this.startScale;
34707             
34708             this.onRotateFail();
34709             
34710             return false;
34711         }
34712         
34713         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34714
34715         if(this.isDocument){
34716             this.setThumbBoxSize();
34717             this.setThumbBoxPosition();
34718             this.setCanvasPosition();
34719         }
34720         
34721         this.draw();
34722         
34723         this.fireEvent('rotate', this, 'right');
34724     },
34725     
34726     onRotateFail : function()
34727     {
34728         this.errorEl.show(true);
34729         
34730         var _this = this;
34731         
34732         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34733     },
34734     
34735     draw : function()
34736     {
34737         this.previewEl.dom.innerHTML = '';
34738         
34739         var canvasEl = document.createElement("canvas");
34740         
34741         var contextEl = canvasEl.getContext("2d");
34742         
34743         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34744         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34745         var center = this.imageEl.OriginWidth / 2;
34746         
34747         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34748             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34749             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34750             center = this.imageEl.OriginHeight / 2;
34751         }
34752         
34753         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34754         
34755         contextEl.translate(center, center);
34756         contextEl.rotate(this.rotate * Math.PI / 180);
34757
34758         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34759         
34760         this.canvasEl = document.createElement("canvas");
34761         
34762         this.contextEl = this.canvasEl.getContext("2d");
34763         
34764         switch (this.rotate) {
34765             case 0 :
34766                 
34767                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34768                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34769                 
34770                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34771                 
34772                 break;
34773             case 90 : 
34774                 
34775                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34776                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34777                 
34778                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34779                     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);
34780                     break;
34781                 }
34782                 
34783                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34784                 
34785                 break;
34786             case 180 :
34787                 
34788                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34789                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34790                 
34791                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34792                     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);
34793                     break;
34794                 }
34795                 
34796                 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);
34797                 
34798                 break;
34799             case 270 :
34800                 
34801                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34802                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34803         
34804                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34805                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34806                     break;
34807                 }
34808                 
34809                 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);
34810                 
34811                 break;
34812             default : 
34813                 break;
34814         }
34815         
34816         this.previewEl.appendChild(this.canvasEl);
34817         
34818         this.setCanvasPosition();
34819     },
34820     
34821     crop : function()
34822     {
34823         if(!this.canvasLoaded){
34824             return;
34825         }
34826         
34827         var imageCanvas = document.createElement("canvas");
34828         
34829         var imageContext = imageCanvas.getContext("2d");
34830         
34831         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34832         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34833         
34834         var center = imageCanvas.width / 2;
34835         
34836         imageContext.translate(center, center);
34837         
34838         imageContext.rotate(this.rotate * Math.PI / 180);
34839         
34840         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34841         
34842         var canvas = document.createElement("canvas");
34843         
34844         var context = canvas.getContext("2d");
34845                 
34846         canvas.width = this.minWidth;
34847         canvas.height = this.minHeight;
34848
34849         switch (this.rotate) {
34850             case 0 :
34851                 
34852                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34853                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34854                 
34855                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34856                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34857                 
34858                 var targetWidth = this.minWidth - 2 * x;
34859                 var targetHeight = this.minHeight - 2 * y;
34860                 
34861                 var scale = 1;
34862                 
34863                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34864                     scale = targetWidth / width;
34865                 }
34866                 
34867                 if(x > 0 && y == 0){
34868                     scale = targetHeight / height;
34869                 }
34870                 
34871                 if(x > 0 && y > 0){
34872                     scale = targetWidth / width;
34873                     
34874                     if(width < height){
34875                         scale = targetHeight / height;
34876                     }
34877                 }
34878                 
34879                 context.scale(scale, scale);
34880                 
34881                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34882                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34883
34884                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34885                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34886
34887                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34888                 
34889                 break;
34890             case 90 : 
34891                 
34892                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34893                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34894                 
34895                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34896                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34897                 
34898                 var targetWidth = this.minWidth - 2 * x;
34899                 var targetHeight = this.minHeight - 2 * y;
34900                 
34901                 var scale = 1;
34902                 
34903                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34904                     scale = targetWidth / width;
34905                 }
34906                 
34907                 if(x > 0 && y == 0){
34908                     scale = targetHeight / height;
34909                 }
34910                 
34911                 if(x > 0 && y > 0){
34912                     scale = targetWidth / width;
34913                     
34914                     if(width < height){
34915                         scale = targetHeight / height;
34916                     }
34917                 }
34918                 
34919                 context.scale(scale, scale);
34920                 
34921                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34922                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34923
34924                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34925                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34926                 
34927                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34928                 
34929                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34930                 
34931                 break;
34932             case 180 :
34933                 
34934                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34935                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34936                 
34937                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34938                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34939                 
34940                 var targetWidth = this.minWidth - 2 * x;
34941                 var targetHeight = this.minHeight - 2 * y;
34942                 
34943                 var scale = 1;
34944                 
34945                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34946                     scale = targetWidth / width;
34947                 }
34948                 
34949                 if(x > 0 && y == 0){
34950                     scale = targetHeight / height;
34951                 }
34952                 
34953                 if(x > 0 && y > 0){
34954                     scale = targetWidth / width;
34955                     
34956                     if(width < height){
34957                         scale = targetHeight / height;
34958                     }
34959                 }
34960                 
34961                 context.scale(scale, scale);
34962                 
34963                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34964                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34965
34966                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34967                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34968
34969                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34970                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34971                 
34972                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34973                 
34974                 break;
34975             case 270 :
34976                 
34977                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34978                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34979                 
34980                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34981                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34982                 
34983                 var targetWidth = this.minWidth - 2 * x;
34984                 var targetHeight = this.minHeight - 2 * y;
34985                 
34986                 var scale = 1;
34987                 
34988                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34989                     scale = targetWidth / width;
34990                 }
34991                 
34992                 if(x > 0 && y == 0){
34993                     scale = targetHeight / height;
34994                 }
34995                 
34996                 if(x > 0 && y > 0){
34997                     scale = targetWidth / width;
34998                     
34999                     if(width < height){
35000                         scale = targetHeight / height;
35001                     }
35002                 }
35003                 
35004                 context.scale(scale, scale);
35005                 
35006                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35007                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35008
35009                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35010                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35011                 
35012                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35013                 
35014                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35015                 
35016                 break;
35017             default : 
35018                 break;
35019         }
35020         
35021         this.cropData = canvas.toDataURL(this.cropType);
35022         
35023         if(this.fireEvent('crop', this, this.cropData) !== false){
35024             this.process(this.file, this.cropData);
35025         }
35026         
35027         return;
35028         
35029     },
35030     
35031     setThumbBoxSize : function()
35032     {
35033         var width, height;
35034         
35035         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35036             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35037             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35038             
35039             this.minWidth = width;
35040             this.minHeight = height;
35041             
35042             if(this.rotate == 90 || this.rotate == 270){
35043                 this.minWidth = height;
35044                 this.minHeight = width;
35045             }
35046         }
35047         
35048         height = 300;
35049         width = Math.ceil(this.minWidth * height / this.minHeight);
35050         
35051         if(this.minWidth > this.minHeight){
35052             width = 300;
35053             height = Math.ceil(this.minHeight * width / this.minWidth);
35054         }
35055         
35056         this.thumbEl.setStyle({
35057             width : width + 'px',
35058             height : height + 'px'
35059         });
35060
35061         return;
35062             
35063     },
35064     
35065     setThumbBoxPosition : function()
35066     {
35067         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35068         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35069         
35070         this.thumbEl.setLeft(x);
35071         this.thumbEl.setTop(y);
35072         
35073     },
35074     
35075     baseRotateLevel : function()
35076     {
35077         this.baseRotate = 1;
35078         
35079         if(
35080                 typeof(this.exif) != 'undefined' &&
35081                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35082                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35083         ){
35084             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35085         }
35086         
35087         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35088         
35089     },
35090     
35091     baseScaleLevel : function()
35092     {
35093         var width, height;
35094         
35095         if(this.isDocument){
35096             
35097             if(this.baseRotate == 6 || this.baseRotate == 8){
35098             
35099                 height = this.thumbEl.getHeight();
35100                 this.baseScale = height / this.imageEl.OriginWidth;
35101
35102                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35103                     width = this.thumbEl.getWidth();
35104                     this.baseScale = width / this.imageEl.OriginHeight;
35105                 }
35106
35107                 return;
35108             }
35109
35110             height = this.thumbEl.getHeight();
35111             this.baseScale = height / this.imageEl.OriginHeight;
35112
35113             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35114                 width = this.thumbEl.getWidth();
35115                 this.baseScale = width / this.imageEl.OriginWidth;
35116             }
35117
35118             return;
35119         }
35120         
35121         if(this.baseRotate == 6 || this.baseRotate == 8){
35122             
35123             width = this.thumbEl.getHeight();
35124             this.baseScale = width / this.imageEl.OriginHeight;
35125             
35126             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35127                 height = this.thumbEl.getWidth();
35128                 this.baseScale = height / this.imageEl.OriginHeight;
35129             }
35130             
35131             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35132                 height = this.thumbEl.getWidth();
35133                 this.baseScale = height / this.imageEl.OriginHeight;
35134                 
35135                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35136                     width = this.thumbEl.getHeight();
35137                     this.baseScale = width / this.imageEl.OriginWidth;
35138                 }
35139             }
35140             
35141             return;
35142         }
35143         
35144         width = this.thumbEl.getWidth();
35145         this.baseScale = width / this.imageEl.OriginWidth;
35146         
35147         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35148             height = this.thumbEl.getHeight();
35149             this.baseScale = height / this.imageEl.OriginHeight;
35150         }
35151         
35152         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35153             
35154             height = this.thumbEl.getHeight();
35155             this.baseScale = height / this.imageEl.OriginHeight;
35156             
35157             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35158                 width = this.thumbEl.getWidth();
35159                 this.baseScale = width / this.imageEl.OriginWidth;
35160             }
35161             
35162         }
35163         
35164         return;
35165     },
35166     
35167     getScaleLevel : function()
35168     {
35169         return this.baseScale * Math.pow(1.1, this.scale);
35170     },
35171     
35172     onTouchStart : function(e)
35173     {
35174         if(!this.canvasLoaded){
35175             this.beforeSelectFile(e);
35176             return;
35177         }
35178         
35179         var touches = e.browserEvent.touches;
35180         
35181         if(!touches){
35182             return;
35183         }
35184         
35185         if(touches.length == 1){
35186             this.onMouseDown(e);
35187             return;
35188         }
35189         
35190         if(touches.length != 2){
35191             return;
35192         }
35193         
35194         var coords = [];
35195         
35196         for(var i = 0, finger; finger = touches[i]; i++){
35197             coords.push(finger.pageX, finger.pageY);
35198         }
35199         
35200         var x = Math.pow(coords[0] - coords[2], 2);
35201         var y = Math.pow(coords[1] - coords[3], 2);
35202         
35203         this.startDistance = Math.sqrt(x + y);
35204         
35205         this.startScale = this.scale;
35206         
35207         this.pinching = true;
35208         this.dragable = false;
35209         
35210     },
35211     
35212     onTouchMove : function(e)
35213     {
35214         if(!this.pinching && !this.dragable){
35215             return;
35216         }
35217         
35218         var touches = e.browserEvent.touches;
35219         
35220         if(!touches){
35221             return;
35222         }
35223         
35224         if(this.dragable){
35225             this.onMouseMove(e);
35226             return;
35227         }
35228         
35229         var coords = [];
35230         
35231         for(var i = 0, finger; finger = touches[i]; i++){
35232             coords.push(finger.pageX, finger.pageY);
35233         }
35234         
35235         var x = Math.pow(coords[0] - coords[2], 2);
35236         var y = Math.pow(coords[1] - coords[3], 2);
35237         
35238         this.endDistance = Math.sqrt(x + y);
35239         
35240         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
35241         
35242         if(!this.zoomable()){
35243             this.scale = this.startScale;
35244             return;
35245         }
35246         
35247         this.draw();
35248         
35249     },
35250     
35251     onTouchEnd : function(e)
35252     {
35253         this.pinching = false;
35254         this.dragable = false;
35255         
35256     },
35257     
35258     process : function(file, crop)
35259     {
35260         if(this.loadMask){
35261             this.maskEl.mask(this.loadingText);
35262         }
35263         
35264         this.xhr = new XMLHttpRequest();
35265         
35266         file.xhr = this.xhr;
35267
35268         this.xhr.open(this.method, this.url, true);
35269         
35270         var headers = {
35271             "Accept": "application/json",
35272             "Cache-Control": "no-cache",
35273             "X-Requested-With": "XMLHttpRequest"
35274         };
35275         
35276         for (var headerName in headers) {
35277             var headerValue = headers[headerName];
35278             if (headerValue) {
35279                 this.xhr.setRequestHeader(headerName, headerValue);
35280             }
35281         }
35282         
35283         var _this = this;
35284         
35285         this.xhr.onload = function()
35286         {
35287             _this.xhrOnLoad(_this.xhr);
35288         }
35289         
35290         this.xhr.onerror = function()
35291         {
35292             _this.xhrOnError(_this.xhr);
35293         }
35294         
35295         var formData = new FormData();
35296
35297         formData.append('returnHTML', 'NO');
35298         
35299         if(crop){
35300             formData.append('crop', crop);
35301         }
35302         
35303         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
35304             formData.append(this.paramName, file, file.name);
35305         }
35306         
35307         if(typeof(file.filename) != 'undefined'){
35308             formData.append('filename', file.filename);
35309         }
35310         
35311         if(typeof(file.mimetype) != 'undefined'){
35312             formData.append('mimetype', file.mimetype);
35313         }
35314         
35315         if(this.fireEvent('arrange', this, formData) != false){
35316             this.xhr.send(formData);
35317         };
35318     },
35319     
35320     xhrOnLoad : function(xhr)
35321     {
35322         if(this.loadMask){
35323             this.maskEl.unmask();
35324         }
35325         
35326         if (xhr.readyState !== 4) {
35327             this.fireEvent('exception', this, xhr);
35328             return;
35329         }
35330
35331         var response = Roo.decode(xhr.responseText);
35332         
35333         if(!response.success){
35334             this.fireEvent('exception', this, xhr);
35335             return;
35336         }
35337         
35338         var response = Roo.decode(xhr.responseText);
35339         
35340         this.fireEvent('upload', this, response);
35341         
35342     },
35343     
35344     xhrOnError : function()
35345     {
35346         if(this.loadMask){
35347             this.maskEl.unmask();
35348         }
35349         
35350         Roo.log('xhr on error');
35351         
35352         var response = Roo.decode(xhr.responseText);
35353           
35354         Roo.log(response);
35355         
35356     },
35357     
35358     prepare : function(file)
35359     {   
35360         if(this.loadMask){
35361             this.maskEl.mask(this.loadingText);
35362         }
35363         
35364         this.file = false;
35365         this.exif = {};
35366         
35367         if(typeof(file) === 'string'){
35368             this.loadCanvas(file);
35369             return;
35370         }
35371         
35372         if(!file || !this.urlAPI){
35373             return;
35374         }
35375         
35376         this.file = file;
35377         this.cropType = file.type;
35378         
35379         var _this = this;
35380         
35381         if(this.fireEvent('prepare', this, this.file) != false){
35382             
35383             var reader = new FileReader();
35384             
35385             reader.onload = function (e) {
35386                 if (e.target.error) {
35387                     Roo.log(e.target.error);
35388                     return;
35389                 }
35390                 
35391                 var buffer = e.target.result,
35392                     dataView = new DataView(buffer),
35393                     offset = 2,
35394                     maxOffset = dataView.byteLength - 4,
35395                     markerBytes,
35396                     markerLength;
35397                 
35398                 if (dataView.getUint16(0) === 0xffd8) {
35399                     while (offset < maxOffset) {
35400                         markerBytes = dataView.getUint16(offset);
35401                         
35402                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
35403                             markerLength = dataView.getUint16(offset + 2) + 2;
35404                             if (offset + markerLength > dataView.byteLength) {
35405                                 Roo.log('Invalid meta data: Invalid segment size.');
35406                                 break;
35407                             }
35408                             
35409                             if(markerBytes == 0xffe1){
35410                                 _this.parseExifData(
35411                                     dataView,
35412                                     offset,
35413                                     markerLength
35414                                 );
35415                             }
35416                             
35417                             offset += markerLength;
35418                             
35419                             continue;
35420                         }
35421                         
35422                         break;
35423                     }
35424                     
35425                 }
35426                 
35427                 var url = _this.urlAPI.createObjectURL(_this.file);
35428                 
35429                 _this.loadCanvas(url);
35430                 
35431                 return;
35432             }
35433             
35434             reader.readAsArrayBuffer(this.file);
35435             
35436         }
35437         
35438     },
35439     
35440     parseExifData : function(dataView, offset, length)
35441     {
35442         var tiffOffset = offset + 10,
35443             littleEndian,
35444             dirOffset;
35445     
35446         if (dataView.getUint32(offset + 4) !== 0x45786966) {
35447             // No Exif data, might be XMP data instead
35448             return;
35449         }
35450         
35451         // Check for the ASCII code for "Exif" (0x45786966):
35452         if (dataView.getUint32(offset + 4) !== 0x45786966) {
35453             // No Exif data, might be XMP data instead
35454             return;
35455         }
35456         if (tiffOffset + 8 > dataView.byteLength) {
35457             Roo.log('Invalid Exif data: Invalid segment size.');
35458             return;
35459         }
35460         // Check for the two null bytes:
35461         if (dataView.getUint16(offset + 8) !== 0x0000) {
35462             Roo.log('Invalid Exif data: Missing byte alignment offset.');
35463             return;
35464         }
35465         // Check the byte alignment:
35466         switch (dataView.getUint16(tiffOffset)) {
35467         case 0x4949:
35468             littleEndian = true;
35469             break;
35470         case 0x4D4D:
35471             littleEndian = false;
35472             break;
35473         default:
35474             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
35475             return;
35476         }
35477         // Check for the TIFF tag marker (0x002A):
35478         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
35479             Roo.log('Invalid Exif data: Missing TIFF marker.');
35480             return;
35481         }
35482         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
35483         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
35484         
35485         this.parseExifTags(
35486             dataView,
35487             tiffOffset,
35488             tiffOffset + dirOffset,
35489             littleEndian
35490         );
35491     },
35492     
35493     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
35494     {
35495         var tagsNumber,
35496             dirEndOffset,
35497             i;
35498         if (dirOffset + 6 > dataView.byteLength) {
35499             Roo.log('Invalid Exif data: Invalid directory offset.');
35500             return;
35501         }
35502         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
35503         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
35504         if (dirEndOffset + 4 > dataView.byteLength) {
35505             Roo.log('Invalid Exif data: Invalid directory size.');
35506             return;
35507         }
35508         for (i = 0; i < tagsNumber; i += 1) {
35509             this.parseExifTag(
35510                 dataView,
35511                 tiffOffset,
35512                 dirOffset + 2 + 12 * i, // tag offset
35513                 littleEndian
35514             );
35515         }
35516         // Return the offset to the next directory:
35517         return dataView.getUint32(dirEndOffset, littleEndian);
35518     },
35519     
35520     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
35521     {
35522         var tag = dataView.getUint16(offset, littleEndian);
35523         
35524         this.exif[tag] = this.getExifValue(
35525             dataView,
35526             tiffOffset,
35527             offset,
35528             dataView.getUint16(offset + 2, littleEndian), // tag type
35529             dataView.getUint32(offset + 4, littleEndian), // tag length
35530             littleEndian
35531         );
35532     },
35533     
35534     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35535     {
35536         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35537             tagSize,
35538             dataOffset,
35539             values,
35540             i,
35541             str,
35542             c;
35543     
35544         if (!tagType) {
35545             Roo.log('Invalid Exif data: Invalid tag type.');
35546             return;
35547         }
35548         
35549         tagSize = tagType.size * length;
35550         // Determine if the value is contained in the dataOffset bytes,
35551         // or if the value at the dataOffset is a pointer to the actual data:
35552         dataOffset = tagSize > 4 ?
35553                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35554         if (dataOffset + tagSize > dataView.byteLength) {
35555             Roo.log('Invalid Exif data: Invalid data offset.');
35556             return;
35557         }
35558         if (length === 1) {
35559             return tagType.getValue(dataView, dataOffset, littleEndian);
35560         }
35561         values = [];
35562         for (i = 0; i < length; i += 1) {
35563             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35564         }
35565         
35566         if (tagType.ascii) {
35567             str = '';
35568             // Concatenate the chars:
35569             for (i = 0; i < values.length; i += 1) {
35570                 c = values[i];
35571                 // Ignore the terminating NULL byte(s):
35572                 if (c === '\u0000') {
35573                     break;
35574                 }
35575                 str += c;
35576             }
35577             return str;
35578         }
35579         return values;
35580     }
35581     
35582 });
35583
35584 Roo.apply(Roo.bootstrap.UploadCropbox, {
35585     tags : {
35586         'Orientation': 0x0112
35587     },
35588     
35589     Orientation: {
35590             1: 0, //'top-left',
35591 //            2: 'top-right',
35592             3: 180, //'bottom-right',
35593 //            4: 'bottom-left',
35594 //            5: 'left-top',
35595             6: 90, //'right-top',
35596 //            7: 'right-bottom',
35597             8: 270 //'left-bottom'
35598     },
35599     
35600     exifTagTypes : {
35601         // byte, 8-bit unsigned int:
35602         1: {
35603             getValue: function (dataView, dataOffset) {
35604                 return dataView.getUint8(dataOffset);
35605             },
35606             size: 1
35607         },
35608         // ascii, 8-bit byte:
35609         2: {
35610             getValue: function (dataView, dataOffset) {
35611                 return String.fromCharCode(dataView.getUint8(dataOffset));
35612             },
35613             size: 1,
35614             ascii: true
35615         },
35616         // short, 16 bit int:
35617         3: {
35618             getValue: function (dataView, dataOffset, littleEndian) {
35619                 return dataView.getUint16(dataOffset, littleEndian);
35620             },
35621             size: 2
35622         },
35623         // long, 32 bit int:
35624         4: {
35625             getValue: function (dataView, dataOffset, littleEndian) {
35626                 return dataView.getUint32(dataOffset, littleEndian);
35627             },
35628             size: 4
35629         },
35630         // rational = two long values, first is numerator, second is denominator:
35631         5: {
35632             getValue: function (dataView, dataOffset, littleEndian) {
35633                 return dataView.getUint32(dataOffset, littleEndian) /
35634                     dataView.getUint32(dataOffset + 4, littleEndian);
35635             },
35636             size: 8
35637         },
35638         // slong, 32 bit signed int:
35639         9: {
35640             getValue: function (dataView, dataOffset, littleEndian) {
35641                 return dataView.getInt32(dataOffset, littleEndian);
35642             },
35643             size: 4
35644         },
35645         // srational, two slongs, first is numerator, second is denominator:
35646         10: {
35647             getValue: function (dataView, dataOffset, littleEndian) {
35648                 return dataView.getInt32(dataOffset, littleEndian) /
35649                     dataView.getInt32(dataOffset + 4, littleEndian);
35650             },
35651             size: 8
35652         }
35653     },
35654     
35655     footer : {
35656         STANDARD : [
35657             {
35658                 tag : 'div',
35659                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35660                 action : 'rotate-left',
35661                 cn : [
35662                     {
35663                         tag : 'button',
35664                         cls : 'btn btn-default',
35665                         html : '<i class="fa fa-undo"></i>'
35666                     }
35667                 ]
35668             },
35669             {
35670                 tag : 'div',
35671                 cls : 'btn-group roo-upload-cropbox-picture',
35672                 action : 'picture',
35673                 cn : [
35674                     {
35675                         tag : 'button',
35676                         cls : 'btn btn-default',
35677                         html : '<i class="fa fa-picture-o"></i>'
35678                     }
35679                 ]
35680             },
35681             {
35682                 tag : 'div',
35683                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35684                 action : 'rotate-right',
35685                 cn : [
35686                     {
35687                         tag : 'button',
35688                         cls : 'btn btn-default',
35689                         html : '<i class="fa fa-repeat"></i>'
35690                     }
35691                 ]
35692             }
35693         ],
35694         DOCUMENT : [
35695             {
35696                 tag : 'div',
35697                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35698                 action : 'rotate-left',
35699                 cn : [
35700                     {
35701                         tag : 'button',
35702                         cls : 'btn btn-default',
35703                         html : '<i class="fa fa-undo"></i>'
35704                     }
35705                 ]
35706             },
35707             {
35708                 tag : 'div',
35709                 cls : 'btn-group roo-upload-cropbox-download',
35710                 action : 'download',
35711                 cn : [
35712                     {
35713                         tag : 'button',
35714                         cls : 'btn btn-default',
35715                         html : '<i class="fa fa-download"></i>'
35716                     }
35717                 ]
35718             },
35719             {
35720                 tag : 'div',
35721                 cls : 'btn-group roo-upload-cropbox-crop',
35722                 action : 'crop',
35723                 cn : [
35724                     {
35725                         tag : 'button',
35726                         cls : 'btn btn-default',
35727                         html : '<i class="fa fa-crop"></i>'
35728                     }
35729                 ]
35730             },
35731             {
35732                 tag : 'div',
35733                 cls : 'btn-group roo-upload-cropbox-trash',
35734                 action : 'trash',
35735                 cn : [
35736                     {
35737                         tag : 'button',
35738                         cls : 'btn btn-default',
35739                         html : '<i class="fa fa-trash"></i>'
35740                     }
35741                 ]
35742             },
35743             {
35744                 tag : 'div',
35745                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35746                 action : 'rotate-right',
35747                 cn : [
35748                     {
35749                         tag : 'button',
35750                         cls : 'btn btn-default',
35751                         html : '<i class="fa fa-repeat"></i>'
35752                     }
35753                 ]
35754             }
35755         ],
35756         ROTATOR : [
35757             {
35758                 tag : 'div',
35759                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35760                 action : 'rotate-left',
35761                 cn : [
35762                     {
35763                         tag : 'button',
35764                         cls : 'btn btn-default',
35765                         html : '<i class="fa fa-undo"></i>'
35766                     }
35767                 ]
35768             },
35769             {
35770                 tag : 'div',
35771                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35772                 action : 'rotate-right',
35773                 cn : [
35774                     {
35775                         tag : 'button',
35776                         cls : 'btn btn-default',
35777                         html : '<i class="fa fa-repeat"></i>'
35778                     }
35779                 ]
35780             }
35781         ]
35782     }
35783 });
35784
35785 /*
35786 * Licence: LGPL
35787 */
35788
35789 /**
35790  * @class Roo.bootstrap.DocumentManager
35791  * @extends Roo.bootstrap.Component
35792  * Bootstrap DocumentManager class
35793  * @cfg {String} paramName default 'imageUpload'
35794  * @cfg {String} toolTipName default 'filename'
35795  * @cfg {String} method default POST
35796  * @cfg {String} url action url
35797  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35798  * @cfg {Boolean} multiple multiple upload default true
35799  * @cfg {Number} thumbSize default 300
35800  * @cfg {String} fieldLabel
35801  * @cfg {Number} labelWidth default 4
35802  * @cfg {String} labelAlign (left|top) default left
35803  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35804 * @cfg {Number} labellg set the width of label (1-12)
35805  * @cfg {Number} labelmd set the width of label (1-12)
35806  * @cfg {Number} labelsm set the width of label (1-12)
35807  * @cfg {Number} labelxs set the width of label (1-12)
35808  * 
35809  * @constructor
35810  * Create a new DocumentManager
35811  * @param {Object} config The config object
35812  */
35813
35814 Roo.bootstrap.DocumentManager = function(config){
35815     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35816     
35817     this.files = [];
35818     this.delegates = [];
35819     
35820     this.addEvents({
35821         /**
35822          * @event initial
35823          * Fire when initial the DocumentManager
35824          * @param {Roo.bootstrap.DocumentManager} this
35825          */
35826         "initial" : true,
35827         /**
35828          * @event inspect
35829          * inspect selected file
35830          * @param {Roo.bootstrap.DocumentManager} this
35831          * @param {File} file
35832          */
35833         "inspect" : true,
35834         /**
35835          * @event exception
35836          * Fire when xhr load exception
35837          * @param {Roo.bootstrap.DocumentManager} this
35838          * @param {XMLHttpRequest} xhr
35839          */
35840         "exception" : true,
35841         /**
35842          * @event afterupload
35843          * Fire when xhr load exception
35844          * @param {Roo.bootstrap.DocumentManager} this
35845          * @param {XMLHttpRequest} xhr
35846          */
35847         "afterupload" : true,
35848         /**
35849          * @event prepare
35850          * prepare the form data
35851          * @param {Roo.bootstrap.DocumentManager} this
35852          * @param {Object} formData
35853          */
35854         "prepare" : true,
35855         /**
35856          * @event remove
35857          * Fire when remove the file
35858          * @param {Roo.bootstrap.DocumentManager} this
35859          * @param {Object} file
35860          */
35861         "remove" : true,
35862         /**
35863          * @event refresh
35864          * Fire after refresh the file
35865          * @param {Roo.bootstrap.DocumentManager} this
35866          */
35867         "refresh" : true,
35868         /**
35869          * @event click
35870          * Fire after click the image
35871          * @param {Roo.bootstrap.DocumentManager} this
35872          * @param {Object} file
35873          */
35874         "click" : true,
35875         /**
35876          * @event edit
35877          * Fire when upload a image and editable set to true
35878          * @param {Roo.bootstrap.DocumentManager} this
35879          * @param {Object} file
35880          */
35881         "edit" : true,
35882         /**
35883          * @event beforeselectfile
35884          * Fire before select file
35885          * @param {Roo.bootstrap.DocumentManager} this
35886          */
35887         "beforeselectfile" : true,
35888         /**
35889          * @event process
35890          * Fire before process file
35891          * @param {Roo.bootstrap.DocumentManager} this
35892          * @param {Object} file
35893          */
35894         "process" : true,
35895         /**
35896          * @event previewrendered
35897          * Fire when preview rendered
35898          * @param {Roo.bootstrap.DocumentManager} this
35899          * @param {Object} file
35900          */
35901         "previewrendered" : true,
35902         /**
35903          */
35904         "previewResize" : true
35905         
35906     });
35907 };
35908
35909 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35910     
35911     boxes : 0,
35912     inputName : '',
35913     thumbSize : 300,
35914     multiple : true,
35915     files : false,
35916     method : 'POST',
35917     url : '',
35918     paramName : 'imageUpload',
35919     toolTipName : 'filename',
35920     fieldLabel : '',
35921     labelWidth : 4,
35922     labelAlign : 'left',
35923     editable : true,
35924     delegates : false,
35925     xhr : false, 
35926     
35927     labellg : 0,
35928     labelmd : 0,
35929     labelsm : 0,
35930     labelxs : 0,
35931     
35932     getAutoCreate : function()
35933     {   
35934         var managerWidget = {
35935             tag : 'div',
35936             cls : 'roo-document-manager',
35937             cn : [
35938                 {
35939                     tag : 'input',
35940                     cls : 'roo-document-manager-selector',
35941                     type : 'file'
35942                 },
35943                 {
35944                     tag : 'div',
35945                     cls : 'roo-document-manager-uploader',
35946                     cn : [
35947                         {
35948                             tag : 'div',
35949                             cls : 'roo-document-manager-upload-btn',
35950                             html : '<i class="fa fa-plus"></i>'
35951                         }
35952                     ]
35953                     
35954                 }
35955             ]
35956         };
35957         
35958         var content = [
35959             {
35960                 tag : 'div',
35961                 cls : 'column col-md-12',
35962                 cn : managerWidget
35963             }
35964         ];
35965         
35966         if(this.fieldLabel.length){
35967             
35968             content = [
35969                 {
35970                     tag : 'div',
35971                     cls : 'column col-md-12',
35972                     html : this.fieldLabel
35973                 },
35974                 {
35975                     tag : 'div',
35976                     cls : 'column col-md-12',
35977                     cn : managerWidget
35978                 }
35979             ];
35980
35981             if(this.labelAlign == 'left'){
35982                 content = [
35983                     {
35984                         tag : 'div',
35985                         cls : 'column',
35986                         html : this.fieldLabel
35987                     },
35988                     {
35989                         tag : 'div',
35990                         cls : 'column',
35991                         cn : managerWidget
35992                     }
35993                 ];
35994                 
35995                 if(this.labelWidth > 12){
35996                     content[0].style = "width: " + this.labelWidth + 'px';
35997                 }
35998
35999                 if(this.labelWidth < 13 && this.labelmd == 0){
36000                     this.labelmd = this.labelWidth;
36001                 }
36002
36003                 if(this.labellg > 0){
36004                     content[0].cls += ' col-lg-' + this.labellg;
36005                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36006                 }
36007
36008                 if(this.labelmd > 0){
36009                     content[0].cls += ' col-md-' + this.labelmd;
36010                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36011                 }
36012
36013                 if(this.labelsm > 0){
36014                     content[0].cls += ' col-sm-' + this.labelsm;
36015                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36016                 }
36017
36018                 if(this.labelxs > 0){
36019                     content[0].cls += ' col-xs-' + this.labelxs;
36020                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36021                 }
36022                 
36023             }
36024         }
36025         
36026         var cfg = {
36027             tag : 'div',
36028             cls : 'row clearfix',
36029             cn : content
36030         };
36031         
36032         return cfg;
36033         
36034     },
36035     
36036     initEvents : function()
36037     {
36038         this.managerEl = this.el.select('.roo-document-manager', true).first();
36039         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36040         
36041         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36042         this.selectorEl.hide();
36043         
36044         if(this.multiple){
36045             this.selectorEl.attr('multiple', 'multiple');
36046         }
36047         
36048         this.selectorEl.on('change', this.onFileSelected, this);
36049         
36050         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36051         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36052         
36053         this.uploader.on('click', this.onUploaderClick, this);
36054         
36055         this.renderProgressDialog();
36056         
36057         var _this = this;
36058         
36059         window.addEventListener("resize", function() { _this.refresh(); } );
36060         
36061         this.fireEvent('initial', this);
36062     },
36063     
36064     renderProgressDialog : function()
36065     {
36066         var _this = this;
36067         
36068         this.progressDialog = new Roo.bootstrap.Modal({
36069             cls : 'roo-document-manager-progress-dialog',
36070             allow_close : false,
36071             animate : false,
36072             title : '',
36073             buttons : [
36074                 {
36075                     name  :'cancel',
36076                     weight : 'danger',
36077                     html : 'Cancel'
36078                 }
36079             ], 
36080             listeners : { 
36081                 btnclick : function() {
36082                     _this.uploadCancel();
36083                     this.hide();
36084                 }
36085             }
36086         });
36087          
36088         this.progressDialog.render(Roo.get(document.body));
36089          
36090         this.progress = new Roo.bootstrap.Progress({
36091             cls : 'roo-document-manager-progress',
36092             active : true,
36093             striped : true
36094         });
36095         
36096         this.progress.render(this.progressDialog.getChildContainer());
36097         
36098         this.progressBar = new Roo.bootstrap.ProgressBar({
36099             cls : 'roo-document-manager-progress-bar',
36100             aria_valuenow : 0,
36101             aria_valuemin : 0,
36102             aria_valuemax : 12,
36103             panel : 'success'
36104         });
36105         
36106         this.progressBar.render(this.progress.getChildContainer());
36107     },
36108     
36109     onUploaderClick : function(e)
36110     {
36111         e.preventDefault();
36112      
36113         if(this.fireEvent('beforeselectfile', this) != false){
36114             this.selectorEl.dom.click();
36115         }
36116         
36117     },
36118     
36119     onFileSelected : function(e)
36120     {
36121         e.preventDefault();
36122         
36123         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36124             return;
36125         }
36126         
36127         Roo.each(this.selectorEl.dom.files, function(file){
36128             if(this.fireEvent('inspect', this, file) != false){
36129                 this.files.push(file);
36130             }
36131         }, this);
36132         
36133         this.queue();
36134         
36135     },
36136     
36137     queue : function()
36138     {
36139         this.selectorEl.dom.value = '';
36140         
36141         if(!this.files || !this.files.length){
36142             return;
36143         }
36144         
36145         if(this.boxes > 0 && this.files.length > this.boxes){
36146             this.files = this.files.slice(0, this.boxes);
36147         }
36148         
36149         this.uploader.show();
36150         
36151         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36152             this.uploader.hide();
36153         }
36154         
36155         var _this = this;
36156         
36157         var files = [];
36158         
36159         var docs = [];
36160         
36161         Roo.each(this.files, function(file){
36162             
36163             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36164                 var f = this.renderPreview(file);
36165                 files.push(f);
36166                 return;
36167             }
36168             
36169             if(file.type.indexOf('image') != -1){
36170                 this.delegates.push(
36171                     (function(){
36172                         _this.process(file);
36173                     }).createDelegate(this)
36174                 );
36175         
36176                 return;
36177             }
36178             
36179             docs.push(
36180                 (function(){
36181                     _this.process(file);
36182                 }).createDelegate(this)
36183             );
36184             
36185         }, this);
36186         
36187         this.files = files;
36188         
36189         this.delegates = this.delegates.concat(docs);
36190         
36191         if(!this.delegates.length){
36192             this.refresh();
36193             return;
36194         }
36195         
36196         this.progressBar.aria_valuemax = this.delegates.length;
36197         
36198         this.arrange();
36199         
36200         return;
36201     },
36202     
36203     arrange : function()
36204     {
36205         if(!this.delegates.length){
36206             this.progressDialog.hide();
36207             this.refresh();
36208             return;
36209         }
36210         
36211         var delegate = this.delegates.shift();
36212         
36213         this.progressDialog.show();
36214         
36215         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
36216         
36217         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
36218         
36219         delegate();
36220     },
36221     
36222     refresh : function()
36223     {
36224         this.uploader.show();
36225         
36226         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36227             this.uploader.hide();
36228         }
36229         
36230         Roo.isTouch ? this.closable(false) : this.closable(true);
36231         
36232         this.fireEvent('refresh', this);
36233     },
36234     
36235     onRemove : function(e, el, o)
36236     {
36237         e.preventDefault();
36238         
36239         this.fireEvent('remove', this, o);
36240         
36241     },
36242     
36243     remove : function(o)
36244     {
36245         var files = [];
36246         
36247         Roo.each(this.files, function(file){
36248             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
36249                 files.push(file);
36250                 return;
36251             }
36252
36253             o.target.remove();
36254
36255         }, this);
36256         
36257         this.files = files;
36258         
36259         this.refresh();
36260     },
36261     
36262     clear : function()
36263     {
36264         Roo.each(this.files, function(file){
36265             if(!file.target){
36266                 return;
36267             }
36268             
36269             file.target.remove();
36270
36271         }, this);
36272         
36273         this.files = [];
36274         
36275         this.refresh();
36276     },
36277     
36278     onClick : function(e, el, o)
36279     {
36280         e.preventDefault();
36281         
36282         this.fireEvent('click', this, o);
36283         
36284     },
36285     
36286     closable : function(closable)
36287     {
36288         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
36289             
36290             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36291             
36292             if(closable){
36293                 el.show();
36294                 return;
36295             }
36296             
36297             el.hide();
36298             
36299         }, this);
36300     },
36301     
36302     xhrOnLoad : function(xhr)
36303     {
36304         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
36305             el.remove();
36306         }, this);
36307         
36308         if (xhr.readyState !== 4) {
36309             this.arrange();
36310             this.fireEvent('exception', this, xhr);
36311             return;
36312         }
36313
36314         var response = Roo.decode(xhr.responseText);
36315         
36316         if(!response.success){
36317             this.arrange();
36318             this.fireEvent('exception', this, xhr);
36319             return;
36320         }
36321         
36322         var file = this.renderPreview(response.data);
36323         
36324         this.files.push(file);
36325         
36326         this.arrange();
36327         
36328         this.fireEvent('afterupload', this, xhr);
36329         
36330     },
36331     
36332     xhrOnError : function(xhr)
36333     {
36334         Roo.log('xhr on error');
36335         
36336         var response = Roo.decode(xhr.responseText);
36337           
36338         Roo.log(response);
36339         
36340         this.arrange();
36341     },
36342     
36343     process : function(file)
36344     {
36345         if(this.fireEvent('process', this, file) !== false){
36346             if(this.editable && file.type.indexOf('image') != -1){
36347                 this.fireEvent('edit', this, file);
36348                 return;
36349             }
36350
36351             this.uploadStart(file, false);
36352
36353             return;
36354         }
36355         
36356     },
36357     
36358     uploadStart : function(file, crop)
36359     {
36360         this.xhr = new XMLHttpRequest();
36361         
36362         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36363             this.arrange();
36364             return;
36365         }
36366         
36367         file.xhr = this.xhr;
36368             
36369         this.managerEl.createChild({
36370             tag : 'div',
36371             cls : 'roo-document-manager-loading',
36372             cn : [
36373                 {
36374                     tag : 'div',
36375                     tooltip : file.name,
36376                     cls : 'roo-document-manager-thumb',
36377                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36378                 }
36379             ]
36380
36381         });
36382
36383         this.xhr.open(this.method, this.url, true);
36384         
36385         var headers = {
36386             "Accept": "application/json",
36387             "Cache-Control": "no-cache",
36388             "X-Requested-With": "XMLHttpRequest"
36389         };
36390         
36391         for (var headerName in headers) {
36392             var headerValue = headers[headerName];
36393             if (headerValue) {
36394                 this.xhr.setRequestHeader(headerName, headerValue);
36395             }
36396         }
36397         
36398         var _this = this;
36399         
36400         this.xhr.onload = function()
36401         {
36402             _this.xhrOnLoad(_this.xhr);
36403         }
36404         
36405         this.xhr.onerror = function()
36406         {
36407             _this.xhrOnError(_this.xhr);
36408         }
36409         
36410         var formData = new FormData();
36411
36412         formData.append('returnHTML', 'NO');
36413         
36414         if(crop){
36415             formData.append('crop', crop);
36416         }
36417         
36418         formData.append(this.paramName, file, file.name);
36419         
36420         var options = {
36421             file : file, 
36422             manually : false
36423         };
36424         
36425         if(this.fireEvent('prepare', this, formData, options) != false){
36426             
36427             if(options.manually){
36428                 return;
36429             }
36430             
36431             this.xhr.send(formData);
36432             return;
36433         };
36434         
36435         this.uploadCancel();
36436     },
36437     
36438     uploadCancel : function()
36439     {
36440         if (this.xhr) {
36441             this.xhr.abort();
36442         }
36443         
36444         this.delegates = [];
36445         
36446         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
36447             el.remove();
36448         }, this);
36449         
36450         this.arrange();
36451     },
36452     
36453     renderPreview : function(file)
36454     {
36455         if(typeof(file.target) != 'undefined' && file.target){
36456             return file;
36457         }
36458         
36459         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
36460         
36461         var previewEl = this.managerEl.createChild({
36462             tag : 'div',
36463             cls : 'roo-document-manager-preview',
36464             cn : [
36465                 {
36466                     tag : 'div',
36467                     tooltip : file[this.toolTipName],
36468                     cls : 'roo-document-manager-thumb',
36469                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
36470                 },
36471                 {
36472                     tag : 'button',
36473                     cls : 'close',
36474                     html : '<i class="fa fa-times-circle"></i>'
36475                 }
36476             ]
36477         });
36478
36479         var close = previewEl.select('button.close', true).first();
36480
36481         close.on('click', this.onRemove, this, file);
36482
36483         file.target = previewEl;
36484
36485         var image = previewEl.select('img', true).first();
36486         
36487         var _this = this;
36488         
36489         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
36490         
36491         image.on('click', this.onClick, this, file);
36492         
36493         this.fireEvent('previewrendered', this, file);
36494         
36495         return file;
36496         
36497     },
36498     
36499     onPreviewLoad : function(file, image)
36500     {
36501         if(typeof(file.target) == 'undefined' || !file.target){
36502             return;
36503         }
36504         
36505         var width = image.dom.naturalWidth || image.dom.width;
36506         var height = image.dom.naturalHeight || image.dom.height;
36507         
36508         if(!this.previewResize) {
36509             return;
36510         }
36511         
36512         if(width > height){
36513             file.target.addClass('wide');
36514             return;
36515         }
36516         
36517         file.target.addClass('tall');
36518         return;
36519         
36520     },
36521     
36522     uploadFromSource : function(file, crop)
36523     {
36524         this.xhr = new XMLHttpRequest();
36525         
36526         this.managerEl.createChild({
36527             tag : 'div',
36528             cls : 'roo-document-manager-loading',
36529             cn : [
36530                 {
36531                     tag : 'div',
36532                     tooltip : file.name,
36533                     cls : 'roo-document-manager-thumb',
36534                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36535                 }
36536             ]
36537
36538         });
36539
36540         this.xhr.open(this.method, this.url, true);
36541         
36542         var headers = {
36543             "Accept": "application/json",
36544             "Cache-Control": "no-cache",
36545             "X-Requested-With": "XMLHttpRequest"
36546         };
36547         
36548         for (var headerName in headers) {
36549             var headerValue = headers[headerName];
36550             if (headerValue) {
36551                 this.xhr.setRequestHeader(headerName, headerValue);
36552             }
36553         }
36554         
36555         var _this = this;
36556         
36557         this.xhr.onload = function()
36558         {
36559             _this.xhrOnLoad(_this.xhr);
36560         }
36561         
36562         this.xhr.onerror = function()
36563         {
36564             _this.xhrOnError(_this.xhr);
36565         }
36566         
36567         var formData = new FormData();
36568
36569         formData.append('returnHTML', 'NO');
36570         
36571         formData.append('crop', crop);
36572         
36573         if(typeof(file.filename) != 'undefined'){
36574             formData.append('filename', file.filename);
36575         }
36576         
36577         if(typeof(file.mimetype) != 'undefined'){
36578             formData.append('mimetype', file.mimetype);
36579         }
36580         
36581         Roo.log(formData);
36582         
36583         if(this.fireEvent('prepare', this, formData) != false){
36584             this.xhr.send(formData);
36585         };
36586     }
36587 });
36588
36589 /*
36590 * Licence: LGPL
36591 */
36592
36593 /**
36594  * @class Roo.bootstrap.DocumentViewer
36595  * @extends Roo.bootstrap.Component
36596  * Bootstrap DocumentViewer class
36597  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36598  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36599  * 
36600  * @constructor
36601  * Create a new DocumentViewer
36602  * @param {Object} config The config object
36603  */
36604
36605 Roo.bootstrap.DocumentViewer = function(config){
36606     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36607     
36608     this.addEvents({
36609         /**
36610          * @event initial
36611          * Fire after initEvent
36612          * @param {Roo.bootstrap.DocumentViewer} this
36613          */
36614         "initial" : true,
36615         /**
36616          * @event click
36617          * Fire after click
36618          * @param {Roo.bootstrap.DocumentViewer} this
36619          */
36620         "click" : true,
36621         /**
36622          * @event download
36623          * Fire after download button
36624          * @param {Roo.bootstrap.DocumentViewer} this
36625          */
36626         "download" : true,
36627         /**
36628          * @event trash
36629          * Fire after trash button
36630          * @param {Roo.bootstrap.DocumentViewer} this
36631          */
36632         "trash" : true
36633         
36634     });
36635 };
36636
36637 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36638     
36639     showDownload : true,
36640     
36641     showTrash : true,
36642     
36643     getAutoCreate : function()
36644     {
36645         var cfg = {
36646             tag : 'div',
36647             cls : 'roo-document-viewer',
36648             cn : [
36649                 {
36650                     tag : 'div',
36651                     cls : 'roo-document-viewer-body',
36652                     cn : [
36653                         {
36654                             tag : 'div',
36655                             cls : 'roo-document-viewer-thumb',
36656                             cn : [
36657                                 {
36658                                     tag : 'img',
36659                                     cls : 'roo-document-viewer-image'
36660                                 }
36661                             ]
36662                         }
36663                     ]
36664                 },
36665                 {
36666                     tag : 'div',
36667                     cls : 'roo-document-viewer-footer',
36668                     cn : {
36669                         tag : 'div',
36670                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36671                         cn : [
36672                             {
36673                                 tag : 'div',
36674                                 cls : 'btn-group roo-document-viewer-download',
36675                                 cn : [
36676                                     {
36677                                         tag : 'button',
36678                                         cls : 'btn btn-default',
36679                                         html : '<i class="fa fa-download"></i>'
36680                                     }
36681                                 ]
36682                             },
36683                             {
36684                                 tag : 'div',
36685                                 cls : 'btn-group roo-document-viewer-trash',
36686                                 cn : [
36687                                     {
36688                                         tag : 'button',
36689                                         cls : 'btn btn-default',
36690                                         html : '<i class="fa fa-trash"></i>'
36691                                     }
36692                                 ]
36693                             }
36694                         ]
36695                     }
36696                 }
36697             ]
36698         };
36699         
36700         return cfg;
36701     },
36702     
36703     initEvents : function()
36704     {
36705         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36706         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36707         
36708         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36709         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36710         
36711         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36712         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36713         
36714         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36715         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36716         
36717         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36718         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36719         
36720         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36721         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36722         
36723         this.bodyEl.on('click', this.onClick, this);
36724         this.downloadBtn.on('click', this.onDownload, this);
36725         this.trashBtn.on('click', this.onTrash, this);
36726         
36727         this.downloadBtn.hide();
36728         this.trashBtn.hide();
36729         
36730         if(this.showDownload){
36731             this.downloadBtn.show();
36732         }
36733         
36734         if(this.showTrash){
36735             this.trashBtn.show();
36736         }
36737         
36738         if(!this.showDownload && !this.showTrash) {
36739             this.footerEl.hide();
36740         }
36741         
36742     },
36743     
36744     initial : function()
36745     {
36746         this.fireEvent('initial', this);
36747         
36748     },
36749     
36750     onClick : function(e)
36751     {
36752         e.preventDefault();
36753         
36754         this.fireEvent('click', this);
36755     },
36756     
36757     onDownload : function(e)
36758     {
36759         e.preventDefault();
36760         
36761         this.fireEvent('download', this);
36762     },
36763     
36764     onTrash : function(e)
36765     {
36766         e.preventDefault();
36767         
36768         this.fireEvent('trash', this);
36769     }
36770     
36771 });
36772 /*
36773  * - LGPL
36774  *
36775  * FieldLabel
36776  * 
36777  */
36778
36779 /**
36780  * @class Roo.bootstrap.form.FieldLabel
36781  * @extends Roo.bootstrap.Component
36782  * Bootstrap FieldLabel class
36783  * @cfg {String} html contents of the element
36784  * @cfg {String} tag tag of the element default label
36785  * @cfg {String} cls class of the element
36786  * @cfg {String} target label target 
36787  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36788  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36789  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36790  * @cfg {String} iconTooltip default "This field is required"
36791  * @cfg {String} indicatorpos (left|right) default left
36792  * 
36793  * @constructor
36794  * Create a new FieldLabel
36795  * @param {Object} config The config object
36796  */
36797
36798 Roo.bootstrap.form.FieldLabel = function(config){
36799     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36800     
36801     this.addEvents({
36802             /**
36803              * @event invalid
36804              * Fires after the field has been marked as invalid.
36805              * @param {Roo.form.FieldLabel} this
36806              * @param {String} msg The validation message
36807              */
36808             invalid : true,
36809             /**
36810              * @event valid
36811              * Fires after the field has been validated with no errors.
36812              * @param {Roo.form.FieldLabel} this
36813              */
36814             valid : true
36815         });
36816 };
36817
36818 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36819     
36820     tag: 'label',
36821     cls: '',
36822     html: '',
36823     target: '',
36824     allowBlank : true,
36825     invalidClass : 'has-warning',
36826     validClass : 'has-success',
36827     iconTooltip : 'This field is required',
36828     indicatorpos : 'left',
36829     
36830     getAutoCreate : function(){
36831         
36832         var cls = "";
36833         if (!this.allowBlank) {
36834             cls  = "visible";
36835         }
36836         
36837         var cfg = {
36838             tag : this.tag,
36839             cls : 'roo-bootstrap-field-label ' + this.cls,
36840             for : this.target,
36841             cn : [
36842                 {
36843                     tag : 'i',
36844                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36845                     tooltip : this.iconTooltip
36846                 },
36847                 {
36848                     tag : 'span',
36849                     html : this.html
36850                 }
36851             ] 
36852         };
36853         
36854         if(this.indicatorpos == 'right'){
36855             var cfg = {
36856                 tag : this.tag,
36857                 cls : 'roo-bootstrap-field-label ' + this.cls,
36858                 for : this.target,
36859                 cn : [
36860                     {
36861                         tag : 'span',
36862                         html : this.html
36863                     },
36864                     {
36865                         tag : 'i',
36866                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36867                         tooltip : this.iconTooltip
36868                     }
36869                 ] 
36870             };
36871         }
36872         
36873         return cfg;
36874     },
36875     
36876     initEvents: function() 
36877     {
36878         Roo.bootstrap.Element.superclass.initEvents.call(this);
36879         
36880         this.indicator = this.indicatorEl();
36881         
36882         if(this.indicator){
36883             this.indicator.removeClass('visible');
36884             this.indicator.addClass('invisible');
36885         }
36886         
36887         Roo.bootstrap.form.FieldLabel.register(this);
36888     },
36889     
36890     indicatorEl : function()
36891     {
36892         var indicator = this.el.select('i.roo-required-indicator',true).first();
36893         
36894         if(!indicator){
36895             return false;
36896         }
36897         
36898         return indicator;
36899         
36900     },
36901     
36902     /**
36903      * Mark this field as valid
36904      */
36905     markValid : function()
36906     {
36907         if(this.indicator){
36908             this.indicator.removeClass('visible');
36909             this.indicator.addClass('invisible');
36910         }
36911         if (Roo.bootstrap.version == 3) {
36912             this.el.removeClass(this.invalidClass);
36913             this.el.addClass(this.validClass);
36914         } else {
36915             this.el.removeClass('is-invalid');
36916             this.el.addClass('is-valid');
36917         }
36918         
36919         
36920         this.fireEvent('valid', this);
36921     },
36922     
36923     /**
36924      * Mark this field as invalid
36925      * @param {String} msg The validation message
36926      */
36927     markInvalid : function(msg)
36928     {
36929         if(this.indicator){
36930             this.indicator.removeClass('invisible');
36931             this.indicator.addClass('visible');
36932         }
36933           if (Roo.bootstrap.version == 3) {
36934             this.el.removeClass(this.validClass);
36935             this.el.addClass(this.invalidClass);
36936         } else {
36937             this.el.removeClass('is-valid');
36938             this.el.addClass('is-invalid');
36939         }
36940         
36941         
36942         this.fireEvent('invalid', this, msg);
36943     }
36944     
36945    
36946 });
36947
36948 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36949     
36950     groups: {},
36951     
36952      /**
36953     * register a FieldLabel Group
36954     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36955     */
36956     register : function(label)
36957     {
36958         if(this.groups.hasOwnProperty(label.target)){
36959             return;
36960         }
36961      
36962         this.groups[label.target] = label;
36963         
36964     },
36965     /**
36966     * fetch a FieldLabel Group based on the target
36967     * @param {string} target
36968     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36969     */
36970     get: function(target) {
36971         if (typeof(this.groups[target]) == 'undefined') {
36972             return false;
36973         }
36974         
36975         return this.groups[target] ;
36976     }
36977 });
36978
36979  
36980
36981  /*
36982  * - LGPL
36983  *
36984  * page DateSplitField.
36985  * 
36986  */
36987
36988
36989 /**
36990  * @class Roo.bootstrap.form.DateSplitField
36991  * @extends Roo.bootstrap.Component
36992  * Bootstrap DateSplitField class
36993  * @cfg {string} fieldLabel - the label associated
36994  * @cfg {Number} labelWidth set the width of label (0-12)
36995  * @cfg {String} labelAlign (top|left)
36996  * @cfg {Boolean} dayAllowBlank (true|false) default false
36997  * @cfg {Boolean} monthAllowBlank (true|false) default false
36998  * @cfg {Boolean} yearAllowBlank (true|false) default false
36999  * @cfg {string} dayPlaceholder 
37000  * @cfg {string} monthPlaceholder
37001  * @cfg {string} yearPlaceholder
37002  * @cfg {string} dayFormat default 'd'
37003  * @cfg {string} monthFormat default 'm'
37004  * @cfg {string} yearFormat default 'Y'
37005  * @cfg {Number} labellg set the width of label (1-12)
37006  * @cfg {Number} labelmd set the width of label (1-12)
37007  * @cfg {Number} labelsm set the width of label (1-12)
37008  * @cfg {Number} labelxs set the width of label (1-12)
37009
37010  *     
37011  * @constructor
37012  * Create a new DateSplitField
37013  * @param {Object} config The config object
37014  */
37015
37016 Roo.bootstrap.form.DateSplitField = function(config){
37017     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37018     
37019     this.addEvents({
37020         // raw events
37021          /**
37022          * @event years
37023          * getting the data of years
37024          * @param {Roo.bootstrap.form.DateSplitField} this
37025          * @param {Object} years
37026          */
37027         "years" : true,
37028         /**
37029          * @event days
37030          * getting the data of days
37031          * @param {Roo.bootstrap.form.DateSplitField} this
37032          * @param {Object} days
37033          */
37034         "days" : true,
37035         /**
37036          * @event invalid
37037          * Fires after the field has been marked as invalid.
37038          * @param {Roo.form.Field} this
37039          * @param {String} msg The validation message
37040          */
37041         invalid : true,
37042        /**
37043          * @event valid
37044          * Fires after the field has been validated with no errors.
37045          * @param {Roo.form.Field} this
37046          */
37047         valid : true
37048     });
37049 };
37050
37051 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37052     
37053     fieldLabel : '',
37054     labelAlign : 'top',
37055     labelWidth : 3,
37056     dayAllowBlank : false,
37057     monthAllowBlank : false,
37058     yearAllowBlank : false,
37059     dayPlaceholder : '',
37060     monthPlaceholder : '',
37061     yearPlaceholder : '',
37062     dayFormat : 'd',
37063     monthFormat : 'm',
37064     yearFormat : 'Y',
37065     isFormField : true,
37066     labellg : 0,
37067     labelmd : 0,
37068     labelsm : 0,
37069     labelxs : 0,
37070     
37071     getAutoCreate : function()
37072     {
37073         var cfg = {
37074             tag : 'div',
37075             cls : 'row roo-date-split-field-group',
37076             cn : [
37077                 {
37078                     tag : 'input',
37079                     type : 'hidden',
37080                     cls : 'form-hidden-field roo-date-split-field-group-value',
37081                     name : this.name
37082                 }
37083             ]
37084         };
37085         
37086         var labelCls = 'col-md-12';
37087         var contentCls = 'col-md-4';
37088         
37089         if(this.fieldLabel){
37090             
37091             var label = {
37092                 tag : 'div',
37093                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37094                 cn : [
37095                     {
37096                         tag : 'label',
37097                         html : this.fieldLabel
37098                     }
37099                 ]
37100             };
37101             
37102             if(this.labelAlign == 'left'){
37103             
37104                 if(this.labelWidth > 12){
37105                     label.style = "width: " + this.labelWidth + 'px';
37106                 }
37107
37108                 if(this.labelWidth < 13 && this.labelmd == 0){
37109                     this.labelmd = this.labelWidth;
37110                 }
37111
37112                 if(this.labellg > 0){
37113                     labelCls = ' col-lg-' + this.labellg;
37114                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37115                 }
37116
37117                 if(this.labelmd > 0){
37118                     labelCls = ' col-md-' + this.labelmd;
37119                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37120                 }
37121
37122                 if(this.labelsm > 0){
37123                     labelCls = ' col-sm-' + this.labelsm;
37124                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37125                 }
37126
37127                 if(this.labelxs > 0){
37128                     labelCls = ' col-xs-' + this.labelxs;
37129                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37130                 }
37131             }
37132             
37133             label.cls += ' ' + labelCls;
37134             
37135             cfg.cn.push(label);
37136         }
37137         
37138         Roo.each(['day', 'month', 'year'], function(t){
37139             cfg.cn.push({
37140                 tag : 'div',
37141                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37142             });
37143         }, this);
37144         
37145         return cfg;
37146     },
37147     
37148     inputEl: function ()
37149     {
37150         return this.el.select('.roo-date-split-field-group-value', true).first();
37151     },
37152     
37153     onRender : function(ct, position) 
37154     {
37155         var _this = this;
37156         
37157         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37158         
37159         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37160         
37161         this.dayField = new Roo.bootstrap.form.ComboBox({
37162             allowBlank : this.dayAllowBlank,
37163             alwaysQuery : true,
37164             displayField : 'value',
37165             editable : false,
37166             fieldLabel : '',
37167             forceSelection : true,
37168             mode : 'local',
37169             placeholder : this.dayPlaceholder,
37170             selectOnFocus : true,
37171             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37172             triggerAction : 'all',
37173             typeAhead : true,
37174             valueField : 'value',
37175             store : new Roo.data.SimpleStore({
37176                 data : (function() {    
37177                     var days = [];
37178                     _this.fireEvent('days', _this, days);
37179                     return days;
37180                 })(),
37181                 fields : [ 'value' ]
37182             }),
37183             listeners : {
37184                 select : function (_self, record, index)
37185                 {
37186                     _this.setValue(_this.getValue());
37187                 }
37188             }
37189         });
37190
37191         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37192         
37193         this.monthField = new Roo.bootstrap.form.MonthField({
37194             after : '<i class=\"fa fa-calendar\"></i>',
37195             allowBlank : this.monthAllowBlank,
37196             placeholder : this.monthPlaceholder,
37197             readOnly : true,
37198             listeners : {
37199                 render : function (_self)
37200                 {
37201                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
37202                         e.preventDefault();
37203                         _self.focus();
37204                     });
37205                 },
37206                 select : function (_self, oldvalue, newvalue)
37207                 {
37208                     _this.setValue(_this.getValue());
37209                 }
37210             }
37211         });
37212         
37213         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
37214         
37215         this.yearField = new Roo.bootstrap.form.ComboBox({
37216             allowBlank : this.yearAllowBlank,
37217             alwaysQuery : true,
37218             displayField : 'value',
37219             editable : false,
37220             fieldLabel : '',
37221             forceSelection : true,
37222             mode : 'local',
37223             placeholder : this.yearPlaceholder,
37224             selectOnFocus : true,
37225             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37226             triggerAction : 'all',
37227             typeAhead : true,
37228             valueField : 'value',
37229             store : new Roo.data.SimpleStore({
37230                 data : (function() {
37231                     var years = [];
37232                     _this.fireEvent('years', _this, years);
37233                     return years;
37234                 })(),
37235                 fields : [ 'value' ]
37236             }),
37237             listeners : {
37238                 select : function (_self, record, index)
37239                 {
37240                     _this.setValue(_this.getValue());
37241                 }
37242             }
37243         });
37244
37245         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
37246     },
37247     
37248     setValue : function(v, format)
37249     {
37250         this.inputEl.dom.value = v;
37251         
37252         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
37253         
37254         var d = Date.parseDate(v, f);
37255         
37256         if(!d){
37257             this.validate();
37258             return;
37259         }
37260         
37261         this.setDay(d.format(this.dayFormat));
37262         this.setMonth(d.format(this.monthFormat));
37263         this.setYear(d.format(this.yearFormat));
37264         
37265         this.validate();
37266         
37267         return;
37268     },
37269     
37270     setDay : function(v)
37271     {
37272         this.dayField.setValue(v);
37273         this.inputEl.dom.value = this.getValue();
37274         this.validate();
37275         return;
37276     },
37277     
37278     setMonth : function(v)
37279     {
37280         this.monthField.setValue(v, true);
37281         this.inputEl.dom.value = this.getValue();
37282         this.validate();
37283         return;
37284     },
37285     
37286     setYear : function(v)
37287     {
37288         this.yearField.setValue(v);
37289         this.inputEl.dom.value = this.getValue();
37290         this.validate();
37291         return;
37292     },
37293     
37294     getDay : function()
37295     {
37296         return this.dayField.getValue();
37297     },
37298     
37299     getMonth : function()
37300     {
37301         return this.monthField.getValue();
37302     },
37303     
37304     getYear : function()
37305     {
37306         return this.yearField.getValue();
37307     },
37308     
37309     getValue : function()
37310     {
37311         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
37312         
37313         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
37314         
37315         return date;
37316     },
37317     
37318     reset : function()
37319     {
37320         this.setDay('');
37321         this.setMonth('');
37322         this.setYear('');
37323         this.inputEl.dom.value = '';
37324         this.validate();
37325         return;
37326     },
37327     
37328     validate : function()
37329     {
37330         var d = this.dayField.validate();
37331         var m = this.monthField.validate();
37332         var y = this.yearField.validate();
37333         
37334         var valid = true;
37335         
37336         if(
37337                 (!this.dayAllowBlank && !d) ||
37338                 (!this.monthAllowBlank && !m) ||
37339                 (!this.yearAllowBlank && !y)
37340         ){
37341             valid = false;
37342         }
37343         
37344         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
37345             return valid;
37346         }
37347         
37348         if(valid){
37349             this.markValid();
37350             return valid;
37351         }
37352         
37353         this.markInvalid();
37354         
37355         return valid;
37356     },
37357     
37358     markValid : function()
37359     {
37360         
37361         var label = this.el.select('label', true).first();
37362         var icon = this.el.select('i.fa-star', true).first();
37363
37364         if(label && icon){
37365             icon.remove();
37366         }
37367         
37368         this.fireEvent('valid', this);
37369     },
37370     
37371      /**
37372      * Mark this field as invalid
37373      * @param {String} msg The validation message
37374      */
37375     markInvalid : function(msg)
37376     {
37377         
37378         var label = this.el.select('label', true).first();
37379         var icon = this.el.select('i.fa-star', true).first();
37380
37381         if(label && !icon){
37382             this.el.select('.roo-date-split-field-label', true).createChild({
37383                 tag : 'i',
37384                 cls : 'text-danger fa fa-lg fa-star',
37385                 tooltip : 'This field is required',
37386                 style : 'margin-right:5px;'
37387             }, label, true);
37388         }
37389         
37390         this.fireEvent('invalid', this, msg);
37391     },
37392     
37393     clearInvalid : function()
37394     {
37395         var label = this.el.select('label', true).first();
37396         var icon = this.el.select('i.fa-star', true).first();
37397
37398         if(label && icon){
37399             icon.remove();
37400         }
37401         
37402         this.fireEvent('valid', this);
37403     },
37404     
37405     getName: function()
37406     {
37407         return this.name;
37408     }
37409     
37410 });
37411
37412  
37413
37414 /**
37415  * @class Roo.bootstrap.LayoutMasonry
37416  * @extends Roo.bootstrap.Component
37417  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
37418  * Bootstrap Layout Masonry class
37419  *
37420  * This is based on 
37421  * http://masonry.desandro.com
37422  *
37423  * The idea is to render all the bricks based on vertical width...
37424  *
37425  * The original code extends 'outlayer' - we might need to use that....
37426
37427  * @constructor
37428  * Create a new Element
37429  * @param {Object} config The config object
37430  */
37431
37432 Roo.bootstrap.LayoutMasonry = function(config){
37433     
37434     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
37435     
37436     this.bricks = [];
37437     
37438     Roo.bootstrap.LayoutMasonry.register(this);
37439     
37440     this.addEvents({
37441         // raw events
37442         /**
37443          * @event layout
37444          * Fire after layout the items
37445          * @param {Roo.bootstrap.LayoutMasonry} this
37446          * @param {Roo.EventObject} e
37447          */
37448         "layout" : true
37449     });
37450     
37451 };
37452
37453 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
37454     
37455     /**
37456      * @cfg {Boolean} isLayoutInstant = no animation?
37457      */   
37458     isLayoutInstant : false, // needed?
37459    
37460     /**
37461      * @cfg {Number} boxWidth  width of the columns
37462      */   
37463     boxWidth : 450,
37464     
37465       /**
37466      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
37467      */   
37468     boxHeight : 0,
37469     
37470     /**
37471      * @cfg {Number} padWidth padding below box..
37472      */   
37473     padWidth : 10, 
37474     
37475     /**
37476      * @cfg {Number} gutter gutter width..
37477      */   
37478     gutter : 10,
37479     
37480      /**
37481      * @cfg {Number} maxCols maximum number of columns
37482      */   
37483     
37484     maxCols: 0,
37485     
37486     /**
37487      * @cfg {Boolean} isAutoInitial defalut true
37488      */   
37489     isAutoInitial : true, 
37490     
37491     containerWidth: 0,
37492     
37493     /**
37494      * @cfg {Boolean} isHorizontal defalut false
37495      */   
37496     isHorizontal : false, 
37497
37498     currentSize : null,
37499     
37500     tag: 'div',
37501     
37502     cls: '',
37503     
37504     bricks: null, //CompositeElement
37505     
37506     cols : 1,
37507     
37508     _isLayoutInited : false,
37509     
37510 //    isAlternative : false, // only use for vertical layout...
37511     
37512     /**
37513      * @cfg {Number} alternativePadWidth padding below box..
37514      */   
37515     alternativePadWidth : 50,
37516     
37517     selectedBrick : [],
37518     
37519     getAutoCreate : function(){
37520         
37521         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
37522         
37523         var cfg = {
37524             tag: this.tag,
37525             cls: 'blog-masonary-wrapper ' + this.cls,
37526             cn : {
37527                 cls : 'mas-boxes masonary'
37528             }
37529         };
37530         
37531         return cfg;
37532     },
37533     
37534     getChildContainer: function( )
37535     {
37536         if (this.boxesEl) {
37537             return this.boxesEl;
37538         }
37539         
37540         this.boxesEl = this.el.select('.mas-boxes').first();
37541         
37542         return this.boxesEl;
37543     },
37544     
37545     
37546     initEvents : function()
37547     {
37548         var _this = this;
37549         
37550         if(this.isAutoInitial){
37551             Roo.log('hook children rendered');
37552             this.on('childrenrendered', function() {
37553                 Roo.log('children rendered');
37554                 _this.initial();
37555             } ,this);
37556         }
37557     },
37558     
37559     initial : function()
37560     {
37561         this.selectedBrick = [];
37562         
37563         this.currentSize = this.el.getBox(true);
37564         
37565         Roo.EventManager.onWindowResize(this.resize, this); 
37566
37567         if(!this.isAutoInitial){
37568             this.layout();
37569             return;
37570         }
37571         
37572         this.layout();
37573         
37574         return;
37575         //this.layout.defer(500,this);
37576         
37577     },
37578     
37579     resize : function()
37580     {
37581         var cs = this.el.getBox(true);
37582         
37583         if (
37584                 this.currentSize.width == cs.width && 
37585                 this.currentSize.x == cs.x && 
37586                 this.currentSize.height == cs.height && 
37587                 this.currentSize.y == cs.y 
37588         ) {
37589             Roo.log("no change in with or X or Y");
37590             return;
37591         }
37592         
37593         this.currentSize = cs;
37594         
37595         this.layout();
37596         
37597     },
37598     
37599     layout : function()
37600     {   
37601         this._resetLayout();
37602         
37603         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37604         
37605         this.layoutItems( isInstant );
37606       
37607         this._isLayoutInited = true;
37608         
37609         this.fireEvent('layout', this);
37610         
37611     },
37612     
37613     _resetLayout : function()
37614     {
37615         if(this.isHorizontal){
37616             this.horizontalMeasureColumns();
37617             return;
37618         }
37619         
37620         this.verticalMeasureColumns();
37621         
37622     },
37623     
37624     verticalMeasureColumns : function()
37625     {
37626         this.getContainerWidth();
37627         
37628 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37629 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37630 //            return;
37631 //        }
37632         
37633         var boxWidth = this.boxWidth + this.padWidth;
37634         
37635         if(this.containerWidth < this.boxWidth){
37636             boxWidth = this.containerWidth
37637         }
37638         
37639         var containerWidth = this.containerWidth;
37640         
37641         var cols = Math.floor(containerWidth / boxWidth);
37642         
37643         this.cols = Math.max( cols, 1 );
37644         
37645         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37646         
37647         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37648         
37649         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37650         
37651         this.colWidth = boxWidth + avail - this.padWidth;
37652         
37653         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37654         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37655     },
37656     
37657     horizontalMeasureColumns : function()
37658     {
37659         this.getContainerWidth();
37660         
37661         var boxWidth = this.boxWidth;
37662         
37663         if(this.containerWidth < boxWidth){
37664             boxWidth = this.containerWidth;
37665         }
37666         
37667         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37668         
37669         this.el.setHeight(boxWidth);
37670         
37671     },
37672     
37673     getContainerWidth : function()
37674     {
37675         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37676     },
37677     
37678     layoutItems : function( isInstant )
37679     {
37680         Roo.log(this.bricks);
37681         
37682         var items = Roo.apply([], this.bricks);
37683         
37684         if(this.isHorizontal){
37685             this._horizontalLayoutItems( items , isInstant );
37686             return;
37687         }
37688         
37689 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37690 //            this._verticalAlternativeLayoutItems( items , isInstant );
37691 //            return;
37692 //        }
37693         
37694         this._verticalLayoutItems( items , isInstant );
37695         
37696     },
37697     
37698     _verticalLayoutItems : function ( items , isInstant)
37699     {
37700         if ( !items || !items.length ) {
37701             return;
37702         }
37703         
37704         var standard = [
37705             ['xs', 'xs', 'xs', 'tall'],
37706             ['xs', 'xs', 'tall'],
37707             ['xs', 'xs', 'sm'],
37708             ['xs', 'xs', 'xs'],
37709             ['xs', 'tall'],
37710             ['xs', 'sm'],
37711             ['xs', 'xs'],
37712             ['xs'],
37713             
37714             ['sm', 'xs', 'xs'],
37715             ['sm', 'xs'],
37716             ['sm'],
37717             
37718             ['tall', 'xs', 'xs', 'xs'],
37719             ['tall', 'xs', 'xs'],
37720             ['tall', 'xs'],
37721             ['tall']
37722             
37723         ];
37724         
37725         var queue = [];
37726         
37727         var boxes = [];
37728         
37729         var box = [];
37730         
37731         Roo.each(items, function(item, k){
37732             
37733             switch (item.size) {
37734                 // these layouts take up a full box,
37735                 case 'md' :
37736                 case 'md-left' :
37737                 case 'md-right' :
37738                 case 'wide' :
37739                     
37740                     if(box.length){
37741                         boxes.push(box);
37742                         box = [];
37743                     }
37744                     
37745                     boxes.push([item]);
37746                     
37747                     break;
37748                     
37749                 case 'xs' :
37750                 case 'sm' :
37751                 case 'tall' :
37752                     
37753                     box.push(item);
37754                     
37755                     break;
37756                 default :
37757                     break;
37758                     
37759             }
37760             
37761         }, this);
37762         
37763         if(box.length){
37764             boxes.push(box);
37765             box = [];
37766         }
37767         
37768         var filterPattern = function(box, length)
37769         {
37770             if(!box.length){
37771                 return;
37772             }
37773             
37774             var match = false;
37775             
37776             var pattern = box.slice(0, length);
37777             
37778             var format = [];
37779             
37780             Roo.each(pattern, function(i){
37781                 format.push(i.size);
37782             }, this);
37783             
37784             Roo.each(standard, function(s){
37785                 
37786                 if(String(s) != String(format)){
37787                     return;
37788                 }
37789                 
37790                 match = true;
37791                 return false;
37792                 
37793             }, this);
37794             
37795             if(!match && length == 1){
37796                 return;
37797             }
37798             
37799             if(!match){
37800                 filterPattern(box, length - 1);
37801                 return;
37802             }
37803                 
37804             queue.push(pattern);
37805
37806             box = box.slice(length, box.length);
37807
37808             filterPattern(box, 4);
37809
37810             return;
37811             
37812         }
37813         
37814         Roo.each(boxes, function(box, k){
37815             
37816             if(!box.length){
37817                 return;
37818             }
37819             
37820             if(box.length == 1){
37821                 queue.push(box);
37822                 return;
37823             }
37824             
37825             filterPattern(box, 4);
37826             
37827         }, this);
37828         
37829         this._processVerticalLayoutQueue( queue, isInstant );
37830         
37831     },
37832     
37833 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37834 //    {
37835 //        if ( !items || !items.length ) {
37836 //            return;
37837 //        }
37838 //
37839 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37840 //        
37841 //    },
37842     
37843     _horizontalLayoutItems : function ( items , isInstant)
37844     {
37845         if ( !items || !items.length || items.length < 3) {
37846             return;
37847         }
37848         
37849         items.reverse();
37850         
37851         var eItems = items.slice(0, 3);
37852         
37853         items = items.slice(3, items.length);
37854         
37855         var standard = [
37856             ['xs', 'xs', 'xs', 'wide'],
37857             ['xs', 'xs', 'wide'],
37858             ['xs', 'xs', 'sm'],
37859             ['xs', 'xs', 'xs'],
37860             ['xs', 'wide'],
37861             ['xs', 'sm'],
37862             ['xs', 'xs'],
37863             ['xs'],
37864             
37865             ['sm', 'xs', 'xs'],
37866             ['sm', 'xs'],
37867             ['sm'],
37868             
37869             ['wide', 'xs', 'xs', 'xs'],
37870             ['wide', 'xs', 'xs'],
37871             ['wide', 'xs'],
37872             ['wide'],
37873             
37874             ['wide-thin']
37875         ];
37876         
37877         var queue = [];
37878         
37879         var boxes = [];
37880         
37881         var box = [];
37882         
37883         Roo.each(items, function(item, k){
37884             
37885             switch (item.size) {
37886                 case 'md' :
37887                 case 'md-left' :
37888                 case 'md-right' :
37889                 case 'tall' :
37890                     
37891                     if(box.length){
37892                         boxes.push(box);
37893                         box = [];
37894                     }
37895                     
37896                     boxes.push([item]);
37897                     
37898                     break;
37899                     
37900                 case 'xs' :
37901                 case 'sm' :
37902                 case 'wide' :
37903                 case 'wide-thin' :
37904                     
37905                     box.push(item);
37906                     
37907                     break;
37908                 default :
37909                     break;
37910                     
37911             }
37912             
37913         }, this);
37914         
37915         if(box.length){
37916             boxes.push(box);
37917             box = [];
37918         }
37919         
37920         var filterPattern = function(box, length)
37921         {
37922             if(!box.length){
37923                 return;
37924             }
37925             
37926             var match = false;
37927             
37928             var pattern = box.slice(0, length);
37929             
37930             var format = [];
37931             
37932             Roo.each(pattern, function(i){
37933                 format.push(i.size);
37934             }, this);
37935             
37936             Roo.each(standard, function(s){
37937                 
37938                 if(String(s) != String(format)){
37939                     return;
37940                 }
37941                 
37942                 match = true;
37943                 return false;
37944                 
37945             }, this);
37946             
37947             if(!match && length == 1){
37948                 return;
37949             }
37950             
37951             if(!match){
37952                 filterPattern(box, length - 1);
37953                 return;
37954             }
37955                 
37956             queue.push(pattern);
37957
37958             box = box.slice(length, box.length);
37959
37960             filterPattern(box, 4);
37961
37962             return;
37963             
37964         }
37965         
37966         Roo.each(boxes, function(box, k){
37967             
37968             if(!box.length){
37969                 return;
37970             }
37971             
37972             if(box.length == 1){
37973                 queue.push(box);
37974                 return;
37975             }
37976             
37977             filterPattern(box, 4);
37978             
37979         }, this);
37980         
37981         
37982         var prune = [];
37983         
37984         var pos = this.el.getBox(true);
37985         
37986         var minX = pos.x;
37987         
37988         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37989         
37990         var hit_end = false;
37991         
37992         Roo.each(queue, function(box){
37993             
37994             if(hit_end){
37995                 
37996                 Roo.each(box, function(b){
37997                 
37998                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37999                     b.el.hide();
38000
38001                 }, this);
38002
38003                 return;
38004             }
38005             
38006             var mx = 0;
38007             
38008             Roo.each(box, function(b){
38009                 
38010                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38011                 b.el.show();
38012
38013                 mx = Math.max(mx, b.x);
38014                 
38015             }, this);
38016             
38017             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38018             
38019             if(maxX < minX){
38020                 
38021                 Roo.each(box, function(b){
38022                 
38023                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38024                     b.el.hide();
38025                     
38026                 }, this);
38027                 
38028                 hit_end = true;
38029                 
38030                 return;
38031             }
38032             
38033             prune.push(box);
38034             
38035         }, this);
38036         
38037         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38038     },
38039     
38040     /** Sets position of item in DOM
38041     * @param {Element} item
38042     * @param {Number} x - horizontal position
38043     * @param {Number} y - vertical position
38044     * @param {Boolean} isInstant - disables transitions
38045     */
38046     _processVerticalLayoutQueue : function( queue, isInstant )
38047     {
38048         var pos = this.el.getBox(true);
38049         var x = pos.x;
38050         var y = pos.y;
38051         var maxY = [];
38052         
38053         for (var i = 0; i < this.cols; i++){
38054             maxY[i] = pos.y;
38055         }
38056         
38057         Roo.each(queue, function(box, k){
38058             
38059             var col = k % this.cols;
38060             
38061             Roo.each(box, function(b,kk){
38062                 
38063                 b.el.position('absolute');
38064                 
38065                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38066                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38067                 
38068                 if(b.size == 'md-left' || b.size == 'md-right'){
38069                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38070                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38071                 }
38072                 
38073                 b.el.setWidth(width);
38074                 b.el.setHeight(height);
38075                 // iframe?
38076                 b.el.select('iframe',true).setSize(width,height);
38077                 
38078             }, this);
38079             
38080             for (var i = 0; i < this.cols; i++){
38081                 
38082                 if(maxY[i] < maxY[col]){
38083                     col = i;
38084                     continue;
38085                 }
38086                 
38087                 col = Math.min(col, i);
38088                 
38089             }
38090             
38091             x = pos.x + col * (this.colWidth + this.padWidth);
38092             
38093             y = maxY[col];
38094             
38095             var positions = [];
38096             
38097             switch (box.length){
38098                 case 1 :
38099                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38100                     break;
38101                 case 2 :
38102                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38103                     break;
38104                 case 3 :
38105                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38106                     break;
38107                 case 4 :
38108                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38109                     break;
38110                 default :
38111                     break;
38112             }
38113             
38114             Roo.each(box, function(b,kk){
38115                 
38116                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38117                 
38118                 var sz = b.el.getSize();
38119                 
38120                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38121                 
38122             }, this);
38123             
38124         }, this);
38125         
38126         var mY = 0;
38127         
38128         for (var i = 0; i < this.cols; i++){
38129             mY = Math.max(mY, maxY[i]);
38130         }
38131         
38132         this.el.setHeight(mY - pos.y);
38133         
38134     },
38135     
38136 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38137 //    {
38138 //        var pos = this.el.getBox(true);
38139 //        var x = pos.x;
38140 //        var y = pos.y;
38141 //        var maxX = pos.right;
38142 //        
38143 //        var maxHeight = 0;
38144 //        
38145 //        Roo.each(items, function(item, k){
38146 //            
38147 //            var c = k % 2;
38148 //            
38149 //            item.el.position('absolute');
38150 //                
38151 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38152 //
38153 //            item.el.setWidth(width);
38154 //
38155 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38156 //
38157 //            item.el.setHeight(height);
38158 //            
38159 //            if(c == 0){
38160 //                item.el.setXY([x, y], isInstant ? false : true);
38161 //            } else {
38162 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38163 //            }
38164 //            
38165 //            y = y + height + this.alternativePadWidth;
38166 //            
38167 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38168 //            
38169 //        }, this);
38170 //        
38171 //        this.el.setHeight(maxHeight);
38172 //        
38173 //    },
38174     
38175     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38176     {
38177         var pos = this.el.getBox(true);
38178         
38179         var minX = pos.x;
38180         var minY = pos.y;
38181         
38182         var maxX = pos.right;
38183         
38184         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38185         
38186         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38187         
38188         Roo.each(queue, function(box, k){
38189             
38190             Roo.each(box, function(b, kk){
38191                 
38192                 b.el.position('absolute');
38193                 
38194                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38195                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38196                 
38197                 if(b.size == 'md-left' || b.size == 'md-right'){
38198                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38199                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38200                 }
38201                 
38202                 b.el.setWidth(width);
38203                 b.el.setHeight(height);
38204                 
38205             }, this);
38206             
38207             if(!box.length){
38208                 return;
38209             }
38210             
38211             var positions = [];
38212             
38213             switch (box.length){
38214                 case 1 :
38215                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
38216                     break;
38217                 case 2 :
38218                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
38219                     break;
38220                 case 3 :
38221                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
38222                     break;
38223                 case 4 :
38224                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
38225                     break;
38226                 default :
38227                     break;
38228             }
38229             
38230             Roo.each(box, function(b,kk){
38231                 
38232                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38233                 
38234                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
38235                 
38236             }, this);
38237             
38238         }, this);
38239         
38240     },
38241     
38242     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
38243     {
38244         Roo.each(eItems, function(b,k){
38245             
38246             b.size = (k == 0) ? 'sm' : 'xs';
38247             b.x = (k == 0) ? 2 : 1;
38248             b.y = (k == 0) ? 2 : 1;
38249             
38250             b.el.position('absolute');
38251             
38252             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38253                 
38254             b.el.setWidth(width);
38255             
38256             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38257             
38258             b.el.setHeight(height);
38259             
38260         }, this);
38261
38262         var positions = [];
38263         
38264         positions.push({
38265             x : maxX - this.unitWidth * 2 - this.gutter,
38266             y : minY
38267         });
38268         
38269         positions.push({
38270             x : maxX - this.unitWidth,
38271             y : minY + (this.unitWidth + this.gutter) * 2
38272         });
38273         
38274         positions.push({
38275             x : maxX - this.unitWidth * 3 - this.gutter * 2,
38276             y : minY
38277         });
38278         
38279         Roo.each(eItems, function(b,k){
38280             
38281             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
38282
38283         }, this);
38284         
38285     },
38286     
38287     getVerticalOneBoxColPositions : function(x, y, box)
38288     {
38289         var pos = [];
38290         
38291         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
38292         
38293         if(box[0].size == 'md-left'){
38294             rand = 0;
38295         }
38296         
38297         if(box[0].size == 'md-right'){
38298             rand = 1;
38299         }
38300         
38301         pos.push({
38302             x : x + (this.unitWidth + this.gutter) * rand,
38303             y : y
38304         });
38305         
38306         return pos;
38307     },
38308     
38309     getVerticalTwoBoxColPositions : function(x, y, box)
38310     {
38311         var pos = [];
38312         
38313         if(box[0].size == 'xs'){
38314             
38315             pos.push({
38316                 x : x,
38317                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
38318             });
38319
38320             pos.push({
38321                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
38322                 y : y
38323             });
38324             
38325             return pos;
38326             
38327         }
38328         
38329         pos.push({
38330             x : x,
38331             y : y
38332         });
38333
38334         pos.push({
38335             x : x + (this.unitWidth + this.gutter) * 2,
38336             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
38337         });
38338         
38339         return pos;
38340         
38341     },
38342     
38343     getVerticalThreeBoxColPositions : function(x, y, box)
38344     {
38345         var pos = [];
38346         
38347         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38348             
38349             pos.push({
38350                 x : x,
38351                 y : y
38352             });
38353
38354             pos.push({
38355                 x : x + (this.unitWidth + this.gutter) * 1,
38356                 y : y
38357             });
38358             
38359             pos.push({
38360                 x : x + (this.unitWidth + this.gutter) * 2,
38361                 y : y
38362             });
38363             
38364             return pos;
38365             
38366         }
38367         
38368         if(box[0].size == 'xs' && box[1].size == 'xs'){
38369             
38370             pos.push({
38371                 x : x,
38372                 y : y
38373             });
38374
38375             pos.push({
38376                 x : x,
38377                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
38378             });
38379             
38380             pos.push({
38381                 x : x + (this.unitWidth + this.gutter) * 1,
38382                 y : y
38383             });
38384             
38385             return pos;
38386             
38387         }
38388         
38389         pos.push({
38390             x : x,
38391             y : y
38392         });
38393
38394         pos.push({
38395             x : x + (this.unitWidth + this.gutter) * 2,
38396             y : y
38397         });
38398
38399         pos.push({
38400             x : x + (this.unitWidth + this.gutter) * 2,
38401             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
38402         });
38403             
38404         return pos;
38405         
38406     },
38407     
38408     getVerticalFourBoxColPositions : function(x, y, box)
38409     {
38410         var pos = [];
38411         
38412         if(box[0].size == 'xs'){
38413             
38414             pos.push({
38415                 x : x,
38416                 y : y
38417             });
38418
38419             pos.push({
38420                 x : x,
38421                 y : y + (this.unitHeight + this.gutter) * 1
38422             });
38423             
38424             pos.push({
38425                 x : x,
38426                 y : y + (this.unitHeight + this.gutter) * 2
38427             });
38428             
38429             pos.push({
38430                 x : x + (this.unitWidth + this.gutter) * 1,
38431                 y : y
38432             });
38433             
38434             return pos;
38435             
38436         }
38437         
38438         pos.push({
38439             x : x,
38440             y : y
38441         });
38442
38443         pos.push({
38444             x : x + (this.unitWidth + this.gutter) * 2,
38445             y : y
38446         });
38447
38448         pos.push({
38449             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
38450             y : y + (this.unitHeight + this.gutter) * 1
38451         });
38452
38453         pos.push({
38454             x : x + (this.unitWidth + this.gutter) * 2,
38455             y : y + (this.unitWidth + this.gutter) * 2
38456         });
38457
38458         return pos;
38459         
38460     },
38461     
38462     getHorizontalOneBoxColPositions : function(maxX, minY, box)
38463     {
38464         var pos = [];
38465         
38466         if(box[0].size == 'md-left'){
38467             pos.push({
38468                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
38469                 y : minY
38470             });
38471             
38472             return pos;
38473         }
38474         
38475         if(box[0].size == 'md-right'){
38476             pos.push({
38477                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
38478                 y : minY + (this.unitWidth + this.gutter) * 1
38479             });
38480             
38481             return pos;
38482         }
38483         
38484         var rand = Math.floor(Math.random() * (4 - box[0].y));
38485         
38486         pos.push({
38487             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38488             y : minY + (this.unitWidth + this.gutter) * rand
38489         });
38490         
38491         return pos;
38492         
38493     },
38494     
38495     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
38496     {
38497         var pos = [];
38498         
38499         if(box[0].size == 'xs'){
38500             
38501             pos.push({
38502                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38503                 y : minY
38504             });
38505
38506             pos.push({
38507                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38508                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
38509             });
38510             
38511             return pos;
38512             
38513         }
38514         
38515         pos.push({
38516             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38517             y : minY
38518         });
38519
38520         pos.push({
38521             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38522             y : minY + (this.unitWidth + this.gutter) * 2
38523         });
38524         
38525         return pos;
38526         
38527     },
38528     
38529     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38530     {
38531         var pos = [];
38532         
38533         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38534             
38535             pos.push({
38536                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38537                 y : minY
38538             });
38539
38540             pos.push({
38541                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38542                 y : minY + (this.unitWidth + this.gutter) * 1
38543             });
38544             
38545             pos.push({
38546                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38547                 y : minY + (this.unitWidth + this.gutter) * 2
38548             });
38549             
38550             return pos;
38551             
38552         }
38553         
38554         if(box[0].size == 'xs' && box[1].size == 'xs'){
38555             
38556             pos.push({
38557                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38558                 y : minY
38559             });
38560
38561             pos.push({
38562                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38563                 y : minY
38564             });
38565             
38566             pos.push({
38567                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38568                 y : minY + (this.unitWidth + this.gutter) * 1
38569             });
38570             
38571             return pos;
38572             
38573         }
38574         
38575         pos.push({
38576             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38577             y : minY
38578         });
38579
38580         pos.push({
38581             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38582             y : minY + (this.unitWidth + this.gutter) * 2
38583         });
38584
38585         pos.push({
38586             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38587             y : minY + (this.unitWidth + this.gutter) * 2
38588         });
38589             
38590         return pos;
38591         
38592     },
38593     
38594     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38595     {
38596         var pos = [];
38597         
38598         if(box[0].size == 'xs'){
38599             
38600             pos.push({
38601                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38602                 y : minY
38603             });
38604
38605             pos.push({
38606                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38607                 y : minY
38608             });
38609             
38610             pos.push({
38611                 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),
38612                 y : minY
38613             });
38614             
38615             pos.push({
38616                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38617                 y : minY + (this.unitWidth + this.gutter) * 1
38618             });
38619             
38620             return pos;
38621             
38622         }
38623         
38624         pos.push({
38625             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38626             y : minY
38627         });
38628         
38629         pos.push({
38630             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38631             y : minY + (this.unitWidth + this.gutter) * 2
38632         });
38633         
38634         pos.push({
38635             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38636             y : minY + (this.unitWidth + this.gutter) * 2
38637         });
38638         
38639         pos.push({
38640             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),
38641             y : minY + (this.unitWidth + this.gutter) * 2
38642         });
38643
38644         return pos;
38645         
38646     },
38647     
38648     /**
38649     * remove a Masonry Brick
38650     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38651     */
38652     removeBrick : function(brick_id)
38653     {
38654         if (!brick_id) {
38655             return;
38656         }
38657         
38658         for (var i = 0; i<this.bricks.length; i++) {
38659             if (this.bricks[i].id == brick_id) {
38660                 this.bricks.splice(i,1);
38661                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38662                 this.initial();
38663             }
38664         }
38665     },
38666     
38667     /**
38668     * adds a Masonry Brick
38669     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38670     */
38671     addBrick : function(cfg)
38672     {
38673         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38674         //this.register(cn);
38675         cn.parentId = this.id;
38676         cn.render(this.el);
38677         return cn;
38678     },
38679     
38680     /**
38681     * register a Masonry Brick
38682     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38683     */
38684     
38685     register : function(brick)
38686     {
38687         this.bricks.push(brick);
38688         brick.masonryId = this.id;
38689     },
38690     
38691     /**
38692     * clear all the Masonry Brick
38693     */
38694     clearAll : function()
38695     {
38696         this.bricks = [];
38697         //this.getChildContainer().dom.innerHTML = "";
38698         this.el.dom.innerHTML = '';
38699     },
38700     
38701     getSelected : function()
38702     {
38703         if (!this.selectedBrick) {
38704             return false;
38705         }
38706         
38707         return this.selectedBrick;
38708     }
38709 });
38710
38711 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38712     
38713     groups: {},
38714      /**
38715     * register a Masonry Layout
38716     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38717     */
38718     
38719     register : function(layout)
38720     {
38721         this.groups[layout.id] = layout;
38722     },
38723     /**
38724     * fetch a  Masonry Layout based on the masonry layout ID
38725     * @param {string} the masonry layout to add
38726     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38727     */
38728     
38729     get: function(layout_id) {
38730         if (typeof(this.groups[layout_id]) == 'undefined') {
38731             return false;
38732         }
38733         return this.groups[layout_id] ;
38734     }
38735     
38736     
38737     
38738 });
38739
38740  
38741
38742  /**
38743  *
38744  * This is based on 
38745  * http://masonry.desandro.com
38746  *
38747  * The idea is to render all the bricks based on vertical width...
38748  *
38749  * The original code extends 'outlayer' - we might need to use that....
38750  * 
38751  */
38752
38753
38754 /**
38755  * @class Roo.bootstrap.LayoutMasonryAuto
38756  * @extends Roo.bootstrap.Component
38757  * Bootstrap Layout Masonry class
38758  * 
38759  * @constructor
38760  * Create a new Element
38761  * @param {Object} config The config object
38762  */
38763
38764 Roo.bootstrap.LayoutMasonryAuto = function(config){
38765     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38766 };
38767
38768 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38769     
38770       /**
38771      * @cfg {Boolean} isFitWidth  - resize the width..
38772      */   
38773     isFitWidth : false,  // options..
38774     /**
38775      * @cfg {Boolean} isOriginLeft = left align?
38776      */   
38777     isOriginLeft : true,
38778     /**
38779      * @cfg {Boolean} isOriginTop = top align?
38780      */   
38781     isOriginTop : false,
38782     /**
38783      * @cfg {Boolean} isLayoutInstant = no animation?
38784      */   
38785     isLayoutInstant : false, // needed?
38786     /**
38787      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38788      */   
38789     isResizingContainer : true,
38790     /**
38791      * @cfg {Number} columnWidth  width of the columns 
38792      */   
38793     
38794     columnWidth : 0,
38795     
38796     /**
38797      * @cfg {Number} maxCols maximum number of columns
38798      */   
38799     
38800     maxCols: 0,
38801     /**
38802      * @cfg {Number} padHeight padding below box..
38803      */   
38804     
38805     padHeight : 10, 
38806     
38807     /**
38808      * @cfg {Boolean} isAutoInitial defalut true
38809      */   
38810     
38811     isAutoInitial : true, 
38812     
38813     // private?
38814     gutter : 0,
38815     
38816     containerWidth: 0,
38817     initialColumnWidth : 0,
38818     currentSize : null,
38819     
38820     colYs : null, // array.
38821     maxY : 0,
38822     padWidth: 10,
38823     
38824     
38825     tag: 'div',
38826     cls: '',
38827     bricks: null, //CompositeElement
38828     cols : 0, // array?
38829     // element : null, // wrapped now this.el
38830     _isLayoutInited : null, 
38831     
38832     
38833     getAutoCreate : function(){
38834         
38835         var cfg = {
38836             tag: this.tag,
38837             cls: 'blog-masonary-wrapper ' + this.cls,
38838             cn : {
38839                 cls : 'mas-boxes masonary'
38840             }
38841         };
38842         
38843         return cfg;
38844     },
38845     
38846     getChildContainer: function( )
38847     {
38848         if (this.boxesEl) {
38849             return this.boxesEl;
38850         }
38851         
38852         this.boxesEl = this.el.select('.mas-boxes').first();
38853         
38854         return this.boxesEl;
38855     },
38856     
38857     
38858     initEvents : function()
38859     {
38860         var _this = this;
38861         
38862         if(this.isAutoInitial){
38863             Roo.log('hook children rendered');
38864             this.on('childrenrendered', function() {
38865                 Roo.log('children rendered');
38866                 _this.initial();
38867             } ,this);
38868         }
38869         
38870     },
38871     
38872     initial : function()
38873     {
38874         this.reloadItems();
38875
38876         this.currentSize = this.el.getBox(true);
38877
38878         /// was window resize... - let's see if this works..
38879         Roo.EventManager.onWindowResize(this.resize, this); 
38880
38881         if(!this.isAutoInitial){
38882             this.layout();
38883             return;
38884         }
38885         
38886         this.layout.defer(500,this);
38887     },
38888     
38889     reloadItems: function()
38890     {
38891         this.bricks = this.el.select('.masonry-brick', true);
38892         
38893         this.bricks.each(function(b) {
38894             //Roo.log(b.getSize());
38895             if (!b.attr('originalwidth')) {
38896                 b.attr('originalwidth',  b.getSize().width);
38897             }
38898             
38899         });
38900         
38901         Roo.log(this.bricks.elements.length);
38902     },
38903     
38904     resize : function()
38905     {
38906         Roo.log('resize');
38907         var cs = this.el.getBox(true);
38908         
38909         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38910             Roo.log("no change in with or X");
38911             return;
38912         }
38913         this.currentSize = cs;
38914         this.layout();
38915     },
38916     
38917     layout : function()
38918     {
38919          Roo.log('layout');
38920         this._resetLayout();
38921         //this._manageStamps();
38922       
38923         // don't animate first layout
38924         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38925         this.layoutItems( isInstant );
38926       
38927         // flag for initalized
38928         this._isLayoutInited = true;
38929     },
38930     
38931     layoutItems : function( isInstant )
38932     {
38933         //var items = this._getItemsForLayout( this.items );
38934         // original code supports filtering layout items.. we just ignore it..
38935         
38936         this._layoutItems( this.bricks , isInstant );
38937       
38938         this._postLayout();
38939     },
38940     _layoutItems : function ( items , isInstant)
38941     {
38942        //this.fireEvent( 'layout', this, items );
38943     
38944
38945         if ( !items || !items.elements.length ) {
38946           // no items, emit event with empty array
38947             return;
38948         }
38949
38950         var queue = [];
38951         items.each(function(item) {
38952             Roo.log("layout item");
38953             Roo.log(item);
38954             // get x/y object from method
38955             var position = this._getItemLayoutPosition( item );
38956             // enqueue
38957             position.item = item;
38958             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38959             queue.push( position );
38960         }, this);
38961       
38962         this._processLayoutQueue( queue );
38963     },
38964     /** Sets position of item in DOM
38965     * @param {Element} item
38966     * @param {Number} x - horizontal position
38967     * @param {Number} y - vertical position
38968     * @param {Boolean} isInstant - disables transitions
38969     */
38970     _processLayoutQueue : function( queue )
38971     {
38972         for ( var i=0, len = queue.length; i < len; i++ ) {
38973             var obj = queue[i];
38974             obj.item.position('absolute');
38975             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38976         }
38977     },
38978       
38979     
38980     /**
38981     * Any logic you want to do after each layout,
38982     * i.e. size the container
38983     */
38984     _postLayout : function()
38985     {
38986         this.resizeContainer();
38987     },
38988     
38989     resizeContainer : function()
38990     {
38991         if ( !this.isResizingContainer ) {
38992             return;
38993         }
38994         var size = this._getContainerSize();
38995         if ( size ) {
38996             this.el.setSize(size.width,size.height);
38997             this.boxesEl.setSize(size.width,size.height);
38998         }
38999     },
39000     
39001     
39002     
39003     _resetLayout : function()
39004     {
39005         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39006         this.colWidth = this.el.getWidth();
39007         //this.gutter = this.el.getWidth(); 
39008         
39009         this.measureColumns();
39010
39011         // reset column Y
39012         var i = this.cols;
39013         this.colYs = [];
39014         while (i--) {
39015             this.colYs.push( 0 );
39016         }
39017     
39018         this.maxY = 0;
39019     },
39020
39021     measureColumns : function()
39022     {
39023         this.getContainerWidth();
39024       // if columnWidth is 0, default to outerWidth of first item
39025         if ( !this.columnWidth ) {
39026             var firstItem = this.bricks.first();
39027             Roo.log(firstItem);
39028             this.columnWidth  = this.containerWidth;
39029             if (firstItem && firstItem.attr('originalwidth') ) {
39030                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39031             }
39032             // columnWidth fall back to item of first element
39033             Roo.log("set column width?");
39034                         this.initialColumnWidth = this.columnWidth  ;
39035
39036             // if first elem has no width, default to size of container
39037             
39038         }
39039         
39040         
39041         if (this.initialColumnWidth) {
39042             this.columnWidth = this.initialColumnWidth;
39043         }
39044         
39045         
39046             
39047         // column width is fixed at the top - however if container width get's smaller we should
39048         // reduce it...
39049         
39050         // this bit calcs how man columns..
39051             
39052         var columnWidth = this.columnWidth += this.gutter;
39053       
39054         // calculate columns
39055         var containerWidth = this.containerWidth + this.gutter;
39056         
39057         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39058         // fix rounding errors, typically with gutters
39059         var excess = columnWidth - containerWidth % columnWidth;
39060         
39061         
39062         // if overshoot is less than a pixel, round up, otherwise floor it
39063         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39064         cols = Math[ mathMethod ]( cols );
39065         this.cols = Math.max( cols, 1 );
39066         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39067         
39068          // padding positioning..
39069         var totalColWidth = this.cols * this.columnWidth;
39070         var padavail = this.containerWidth - totalColWidth;
39071         // so for 2 columns - we need 3 'pads'
39072         
39073         var padNeeded = (1+this.cols) * this.padWidth;
39074         
39075         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39076         
39077         this.columnWidth += padExtra
39078         //this.padWidth = Math.floor(padavail /  ( this.cols));
39079         
39080         // adjust colum width so that padding is fixed??
39081         
39082         // we have 3 columns ... total = width * 3
39083         // we have X left over... that should be used by 
39084         
39085         //if (this.expandC) {
39086             
39087         //}
39088         
39089         
39090         
39091     },
39092     
39093     getContainerWidth : function()
39094     {
39095        /* // container is parent if fit width
39096         var container = this.isFitWidth ? this.element.parentNode : this.element;
39097         // check that this.size and size are there
39098         // IE8 triggers resize on body size change, so they might not be
39099         
39100         var size = getSize( container );  //FIXME
39101         this.containerWidth = size && size.innerWidth; //FIXME
39102         */
39103          
39104         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39105         
39106     },
39107     
39108     _getItemLayoutPosition : function( item )  // what is item?
39109     {
39110         // we resize the item to our columnWidth..
39111       
39112         item.setWidth(this.columnWidth);
39113         item.autoBoxAdjust  = false;
39114         
39115         var sz = item.getSize();
39116  
39117         // how many columns does this brick span
39118         var remainder = this.containerWidth % this.columnWidth;
39119         
39120         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39121         // round if off by 1 pixel, otherwise use ceil
39122         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39123         colSpan = Math.min( colSpan, this.cols );
39124         
39125         // normally this should be '1' as we dont' currently allow multi width columns..
39126         
39127         var colGroup = this._getColGroup( colSpan );
39128         // get the minimum Y value from the columns
39129         var minimumY = Math.min.apply( Math, colGroup );
39130         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39131         
39132         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39133          
39134         // position the brick
39135         var position = {
39136             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39137             y: this.currentSize.y + minimumY + this.padHeight
39138         };
39139         
39140         Roo.log(position);
39141         // apply setHeight to necessary columns
39142         var setHeight = minimumY + sz.height + this.padHeight;
39143         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39144         
39145         var setSpan = this.cols + 1 - colGroup.length;
39146         for ( var i = 0; i < setSpan; i++ ) {
39147           this.colYs[ shortColIndex + i ] = setHeight ;
39148         }
39149       
39150         return position;
39151     },
39152     
39153     /**
39154      * @param {Number} colSpan - number of columns the element spans
39155      * @returns {Array} colGroup
39156      */
39157     _getColGroup : function( colSpan )
39158     {
39159         if ( colSpan < 2 ) {
39160           // if brick spans only one column, use all the column Ys
39161           return this.colYs;
39162         }
39163       
39164         var colGroup = [];
39165         // how many different places could this brick fit horizontally
39166         var groupCount = this.cols + 1 - colSpan;
39167         // for each group potential horizontal position
39168         for ( var i = 0; i < groupCount; i++ ) {
39169           // make an array of colY values for that one group
39170           var groupColYs = this.colYs.slice( i, i + colSpan );
39171           // and get the max value of the array
39172           colGroup[i] = Math.max.apply( Math, groupColYs );
39173         }
39174         return colGroup;
39175     },
39176     /*
39177     _manageStamp : function( stamp )
39178     {
39179         var stampSize =  stamp.getSize();
39180         var offset = stamp.getBox();
39181         // get the columns that this stamp affects
39182         var firstX = this.isOriginLeft ? offset.x : offset.right;
39183         var lastX = firstX + stampSize.width;
39184         var firstCol = Math.floor( firstX / this.columnWidth );
39185         firstCol = Math.max( 0, firstCol );
39186         
39187         var lastCol = Math.floor( lastX / this.columnWidth );
39188         // lastCol should not go over if multiple of columnWidth #425
39189         lastCol -= lastX % this.columnWidth ? 0 : 1;
39190         lastCol = Math.min( this.cols - 1, lastCol );
39191         
39192         // set colYs to bottom of the stamp
39193         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39194             stampSize.height;
39195             
39196         for ( var i = firstCol; i <= lastCol; i++ ) {
39197           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39198         }
39199     },
39200     */
39201     
39202     _getContainerSize : function()
39203     {
39204         this.maxY = Math.max.apply( Math, this.colYs );
39205         var size = {
39206             height: this.maxY
39207         };
39208       
39209         if ( this.isFitWidth ) {
39210             size.width = this._getContainerFitWidth();
39211         }
39212       
39213         return size;
39214     },
39215     
39216     _getContainerFitWidth : function()
39217     {
39218         var unusedCols = 0;
39219         // count unused columns
39220         var i = this.cols;
39221         while ( --i ) {
39222           if ( this.colYs[i] !== 0 ) {
39223             break;
39224           }
39225           unusedCols++;
39226         }
39227         // fit container to columns that have been used
39228         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
39229     },
39230     
39231     needsResizeLayout : function()
39232     {
39233         var previousWidth = this.containerWidth;
39234         this.getContainerWidth();
39235         return previousWidth !== this.containerWidth;
39236     }
39237  
39238 });
39239
39240  
39241
39242  /*
39243  * - LGPL
39244  *
39245  * element
39246  * 
39247  */
39248
39249 /**
39250  * @class Roo.bootstrap.MasonryBrick
39251  * @extends Roo.bootstrap.Component
39252  * Bootstrap MasonryBrick class
39253  * 
39254  * @constructor
39255  * Create a new MasonryBrick
39256  * @param {Object} config The config object
39257  */
39258
39259 Roo.bootstrap.MasonryBrick = function(config){
39260     
39261     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
39262     
39263     Roo.bootstrap.MasonryBrick.register(this);
39264     
39265     this.addEvents({
39266         // raw events
39267         /**
39268          * @event click
39269          * When a MasonryBrick is clcik
39270          * @param {Roo.bootstrap.MasonryBrick} this
39271          * @param {Roo.EventObject} e
39272          */
39273         "click" : true
39274     });
39275 };
39276
39277 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
39278     
39279     /**
39280      * @cfg {String} title
39281      */   
39282     title : '',
39283     /**
39284      * @cfg {String} html
39285      */   
39286     html : '',
39287     /**
39288      * @cfg {String} bgimage
39289      */   
39290     bgimage : '',
39291     /**
39292      * @cfg {String} videourl
39293      */   
39294     videourl : '',
39295     /**
39296      * @cfg {String} cls
39297      */   
39298     cls : '',
39299     /**
39300      * @cfg {String} href
39301      */   
39302     href : '',
39303     /**
39304      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
39305      */   
39306     size : 'xs',
39307     
39308     /**
39309      * @cfg {String} placetitle (center|bottom)
39310      */   
39311     placetitle : '',
39312     
39313     /**
39314      * @cfg {Boolean} isFitContainer defalut true
39315      */   
39316     isFitContainer : true, 
39317     
39318     /**
39319      * @cfg {Boolean} preventDefault defalut false
39320      */   
39321     preventDefault : false, 
39322     
39323     /**
39324      * @cfg {Boolean} inverse defalut false
39325      */   
39326     maskInverse : false, 
39327     
39328     getAutoCreate : function()
39329     {
39330         if(!this.isFitContainer){
39331             return this.getSplitAutoCreate();
39332         }
39333         
39334         var cls = 'masonry-brick masonry-brick-full';
39335         
39336         if(this.href.length){
39337             cls += ' masonry-brick-link';
39338         }
39339         
39340         if(this.bgimage.length){
39341             cls += ' masonry-brick-image';
39342         }
39343         
39344         if(this.maskInverse){
39345             cls += ' mask-inverse';
39346         }
39347         
39348         if(!this.html.length && !this.maskInverse && !this.videourl.length){
39349             cls += ' enable-mask';
39350         }
39351         
39352         if(this.size){
39353             cls += ' masonry-' + this.size + '-brick';
39354         }
39355         
39356         if(this.placetitle.length){
39357             
39358             switch (this.placetitle) {
39359                 case 'center' :
39360                     cls += ' masonry-center-title';
39361                     break;
39362                 case 'bottom' :
39363                     cls += ' masonry-bottom-title';
39364                     break;
39365                 default:
39366                     break;
39367             }
39368             
39369         } else {
39370             if(!this.html.length && !this.bgimage.length){
39371                 cls += ' masonry-center-title';
39372             }
39373
39374             if(!this.html.length && this.bgimage.length){
39375                 cls += ' masonry-bottom-title';
39376             }
39377         }
39378         
39379         if(this.cls){
39380             cls += ' ' + this.cls;
39381         }
39382         
39383         var cfg = {
39384             tag: (this.href.length) ? 'a' : 'div',
39385             cls: cls,
39386             cn: [
39387                 {
39388                     tag: 'div',
39389                     cls: 'masonry-brick-mask'
39390                 },
39391                 {
39392                     tag: 'div',
39393                     cls: 'masonry-brick-paragraph',
39394                     cn: []
39395                 }
39396             ]
39397         };
39398         
39399         if(this.href.length){
39400             cfg.href = this.href;
39401         }
39402         
39403         var cn = cfg.cn[1].cn;
39404         
39405         if(this.title.length){
39406             cn.push({
39407                 tag: 'h4',
39408                 cls: 'masonry-brick-title',
39409                 html: this.title
39410             });
39411         }
39412         
39413         if(this.html.length){
39414             cn.push({
39415                 tag: 'p',
39416                 cls: 'masonry-brick-text',
39417                 html: this.html
39418             });
39419         }
39420         
39421         if (!this.title.length && !this.html.length) {
39422             cfg.cn[1].cls += ' hide';
39423         }
39424         
39425         if(this.bgimage.length){
39426             cfg.cn.push({
39427                 tag: 'img',
39428                 cls: 'masonry-brick-image-view',
39429                 src: this.bgimage
39430             });
39431         }
39432         
39433         if(this.videourl.length){
39434             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39435             // youtube support only?
39436             cfg.cn.push({
39437                 tag: 'iframe',
39438                 cls: 'masonry-brick-image-view',
39439                 src: vurl,
39440                 frameborder : 0,
39441                 allowfullscreen : true
39442             });
39443         }
39444         
39445         return cfg;
39446         
39447     },
39448     
39449     getSplitAutoCreate : function()
39450     {
39451         var cls = 'masonry-brick masonry-brick-split';
39452         
39453         if(this.href.length){
39454             cls += ' masonry-brick-link';
39455         }
39456         
39457         if(this.bgimage.length){
39458             cls += ' masonry-brick-image';
39459         }
39460         
39461         if(this.size){
39462             cls += ' masonry-' + this.size + '-brick';
39463         }
39464         
39465         switch (this.placetitle) {
39466             case 'center' :
39467                 cls += ' masonry-center-title';
39468                 break;
39469             case 'bottom' :
39470                 cls += ' masonry-bottom-title';
39471                 break;
39472             default:
39473                 if(!this.bgimage.length){
39474                     cls += ' masonry-center-title';
39475                 }
39476
39477                 if(this.bgimage.length){
39478                     cls += ' masonry-bottom-title';
39479                 }
39480                 break;
39481         }
39482         
39483         if(this.cls){
39484             cls += ' ' + this.cls;
39485         }
39486         
39487         var cfg = {
39488             tag: (this.href.length) ? 'a' : 'div',
39489             cls: cls,
39490             cn: [
39491                 {
39492                     tag: 'div',
39493                     cls: 'masonry-brick-split-head',
39494                     cn: [
39495                         {
39496                             tag: 'div',
39497                             cls: 'masonry-brick-paragraph',
39498                             cn: []
39499                         }
39500                     ]
39501                 },
39502                 {
39503                     tag: 'div',
39504                     cls: 'masonry-brick-split-body',
39505                     cn: []
39506                 }
39507             ]
39508         };
39509         
39510         if(this.href.length){
39511             cfg.href = this.href;
39512         }
39513         
39514         if(this.title.length){
39515             cfg.cn[0].cn[0].cn.push({
39516                 tag: 'h4',
39517                 cls: 'masonry-brick-title',
39518                 html: this.title
39519             });
39520         }
39521         
39522         if(this.html.length){
39523             cfg.cn[1].cn.push({
39524                 tag: 'p',
39525                 cls: 'masonry-brick-text',
39526                 html: this.html
39527             });
39528         }
39529
39530         if(this.bgimage.length){
39531             cfg.cn[0].cn.push({
39532                 tag: 'img',
39533                 cls: 'masonry-brick-image-view',
39534                 src: this.bgimage
39535             });
39536         }
39537         
39538         if(this.videourl.length){
39539             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39540             // youtube support only?
39541             cfg.cn[0].cn.cn.push({
39542                 tag: 'iframe',
39543                 cls: 'masonry-brick-image-view',
39544                 src: vurl,
39545                 frameborder : 0,
39546                 allowfullscreen : true
39547             });
39548         }
39549         
39550         return cfg;
39551     },
39552     
39553     initEvents: function() 
39554     {
39555         switch (this.size) {
39556             case 'xs' :
39557                 this.x = 1;
39558                 this.y = 1;
39559                 break;
39560             case 'sm' :
39561                 this.x = 2;
39562                 this.y = 2;
39563                 break;
39564             case 'md' :
39565             case 'md-left' :
39566             case 'md-right' :
39567                 this.x = 3;
39568                 this.y = 3;
39569                 break;
39570             case 'tall' :
39571                 this.x = 2;
39572                 this.y = 3;
39573                 break;
39574             case 'wide' :
39575                 this.x = 3;
39576                 this.y = 2;
39577                 break;
39578             case 'wide-thin' :
39579                 this.x = 3;
39580                 this.y = 1;
39581                 break;
39582                         
39583             default :
39584                 break;
39585         }
39586         
39587         if(Roo.isTouch){
39588             this.el.on('touchstart', this.onTouchStart, this);
39589             this.el.on('touchmove', this.onTouchMove, this);
39590             this.el.on('touchend', this.onTouchEnd, this);
39591             this.el.on('contextmenu', this.onContextMenu, this);
39592         } else {
39593             this.el.on('mouseenter'  ,this.enter, this);
39594             this.el.on('mouseleave', this.leave, this);
39595             this.el.on('click', this.onClick, this);
39596         }
39597         
39598         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39599             this.parent().bricks.push(this);   
39600         }
39601         
39602     },
39603     
39604     onClick: function(e, el)
39605     {
39606         var time = this.endTimer - this.startTimer;
39607         // Roo.log(e.preventDefault());
39608         if(Roo.isTouch){
39609             if(time > 1000){
39610                 e.preventDefault();
39611                 return;
39612             }
39613         }
39614         
39615         if(!this.preventDefault){
39616             return;
39617         }
39618         
39619         e.preventDefault();
39620         
39621         if (this.activeClass != '') {
39622             this.selectBrick();
39623         }
39624         
39625         this.fireEvent('click', this, e);
39626     },
39627     
39628     enter: function(e, el)
39629     {
39630         e.preventDefault();
39631         
39632         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39633             return;
39634         }
39635         
39636         if(this.bgimage.length && this.html.length){
39637             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39638         }
39639     },
39640     
39641     leave: function(e, el)
39642     {
39643         e.preventDefault();
39644         
39645         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39646             return;
39647         }
39648         
39649         if(this.bgimage.length && this.html.length){
39650             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39651         }
39652     },
39653     
39654     onTouchStart: function(e, el)
39655     {
39656 //        e.preventDefault();
39657         
39658         this.touchmoved = false;
39659         
39660         if(!this.isFitContainer){
39661             return;
39662         }
39663         
39664         if(!this.bgimage.length || !this.html.length){
39665             return;
39666         }
39667         
39668         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39669         
39670         this.timer = new Date().getTime();
39671         
39672     },
39673     
39674     onTouchMove: function(e, el)
39675     {
39676         this.touchmoved = true;
39677     },
39678     
39679     onContextMenu : function(e,el)
39680     {
39681         e.preventDefault();
39682         e.stopPropagation();
39683         return false;
39684     },
39685     
39686     onTouchEnd: function(e, el)
39687     {
39688 //        e.preventDefault();
39689         
39690         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39691         
39692             this.leave(e,el);
39693             
39694             return;
39695         }
39696         
39697         if(!this.bgimage.length || !this.html.length){
39698             
39699             if(this.href.length){
39700                 window.location.href = this.href;
39701             }
39702             
39703             return;
39704         }
39705         
39706         if(!this.isFitContainer){
39707             return;
39708         }
39709         
39710         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39711         
39712         window.location.href = this.href;
39713     },
39714     
39715     //selection on single brick only
39716     selectBrick : function() {
39717         
39718         if (!this.parentId) {
39719             return;
39720         }
39721         
39722         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39723         var index = m.selectedBrick.indexOf(this.id);
39724         
39725         if ( index > -1) {
39726             m.selectedBrick.splice(index,1);
39727             this.el.removeClass(this.activeClass);
39728             return;
39729         }
39730         
39731         for(var i = 0; i < m.selectedBrick.length; i++) {
39732             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39733             b.el.removeClass(b.activeClass);
39734         }
39735         
39736         m.selectedBrick = [];
39737         
39738         m.selectedBrick.push(this.id);
39739         this.el.addClass(this.activeClass);
39740         return;
39741     },
39742     
39743     isSelected : function(){
39744         return this.el.hasClass(this.activeClass);
39745         
39746     }
39747 });
39748
39749 Roo.apply(Roo.bootstrap.MasonryBrick, {
39750     
39751     //groups: {},
39752     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39753      /**
39754     * register a Masonry Brick
39755     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39756     */
39757     
39758     register : function(brick)
39759     {
39760         //this.groups[brick.id] = brick;
39761         this.groups.add(brick.id, brick);
39762     },
39763     /**
39764     * fetch a  masonry brick based on the masonry brick ID
39765     * @param {string} the masonry brick to add
39766     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39767     */
39768     
39769     get: function(brick_id) 
39770     {
39771         // if (typeof(this.groups[brick_id]) == 'undefined') {
39772         //     return false;
39773         // }
39774         // return this.groups[brick_id] ;
39775         
39776         if(this.groups.key(brick_id)) {
39777             return this.groups.key(brick_id);
39778         }
39779         
39780         return false;
39781     }
39782     
39783     
39784     
39785 });
39786
39787  /*
39788  * - LGPL
39789  *
39790  * element
39791  * 
39792  */
39793
39794 /**
39795  * @class Roo.bootstrap.Brick
39796  * @extends Roo.bootstrap.Component
39797  * Bootstrap Brick class
39798  * 
39799  * @constructor
39800  * Create a new Brick
39801  * @param {Object} config The config object
39802  */
39803
39804 Roo.bootstrap.Brick = function(config){
39805     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39806     
39807     this.addEvents({
39808         // raw events
39809         /**
39810          * @event click
39811          * When a Brick is click
39812          * @param {Roo.bootstrap.Brick} this
39813          * @param {Roo.EventObject} e
39814          */
39815         "click" : true
39816     });
39817 };
39818
39819 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39820     
39821     /**
39822      * @cfg {String} title
39823      */   
39824     title : '',
39825     /**
39826      * @cfg {String} html
39827      */   
39828     html : '',
39829     /**
39830      * @cfg {String} bgimage
39831      */   
39832     bgimage : '',
39833     /**
39834      * @cfg {String} cls
39835      */   
39836     cls : '',
39837     /**
39838      * @cfg {String} href
39839      */   
39840     href : '',
39841     /**
39842      * @cfg {String} video
39843      */   
39844     video : '',
39845     /**
39846      * @cfg {Boolean} square
39847      */   
39848     square : true,
39849     
39850     getAutoCreate : function()
39851     {
39852         var cls = 'roo-brick';
39853         
39854         if(this.href.length){
39855             cls += ' roo-brick-link';
39856         }
39857         
39858         if(this.bgimage.length){
39859             cls += ' roo-brick-image';
39860         }
39861         
39862         if(!this.html.length && !this.bgimage.length){
39863             cls += ' roo-brick-center-title';
39864         }
39865         
39866         if(!this.html.length && this.bgimage.length){
39867             cls += ' roo-brick-bottom-title';
39868         }
39869         
39870         if(this.cls){
39871             cls += ' ' + this.cls;
39872         }
39873         
39874         var cfg = {
39875             tag: (this.href.length) ? 'a' : 'div',
39876             cls: cls,
39877             cn: [
39878                 {
39879                     tag: 'div',
39880                     cls: 'roo-brick-paragraph',
39881                     cn: []
39882                 }
39883             ]
39884         };
39885         
39886         if(this.href.length){
39887             cfg.href = this.href;
39888         }
39889         
39890         var cn = cfg.cn[0].cn;
39891         
39892         if(this.title.length){
39893             cn.push({
39894                 tag: 'h4',
39895                 cls: 'roo-brick-title',
39896                 html: this.title
39897             });
39898         }
39899         
39900         if(this.html.length){
39901             cn.push({
39902                 tag: 'p',
39903                 cls: 'roo-brick-text',
39904                 html: this.html
39905             });
39906         } else {
39907             cn.cls += ' hide';
39908         }
39909         
39910         if(this.bgimage.length){
39911             cfg.cn.push({
39912                 tag: 'img',
39913                 cls: 'roo-brick-image-view',
39914                 src: this.bgimage
39915             });
39916         }
39917         
39918         return cfg;
39919     },
39920     
39921     initEvents: function() 
39922     {
39923         if(this.title.length || this.html.length){
39924             this.el.on('mouseenter'  ,this.enter, this);
39925             this.el.on('mouseleave', this.leave, this);
39926         }
39927         
39928         Roo.EventManager.onWindowResize(this.resize, this); 
39929         
39930         if(this.bgimage.length){
39931             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39932             this.imageEl.on('load', this.onImageLoad, this);
39933             return;
39934         }
39935         
39936         this.resize();
39937     },
39938     
39939     onImageLoad : function()
39940     {
39941         this.resize();
39942     },
39943     
39944     resize : function()
39945     {
39946         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39947         
39948         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39949         
39950         if(this.bgimage.length){
39951             var image = this.el.select('.roo-brick-image-view', true).first();
39952             
39953             image.setWidth(paragraph.getWidth());
39954             
39955             if(this.square){
39956                 image.setHeight(paragraph.getWidth());
39957             }
39958             
39959             this.el.setHeight(image.getHeight());
39960             paragraph.setHeight(image.getHeight());
39961             
39962         }
39963         
39964     },
39965     
39966     enter: function(e, el)
39967     {
39968         e.preventDefault();
39969         
39970         if(this.bgimage.length){
39971             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39972             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39973         }
39974     },
39975     
39976     leave: function(e, el)
39977     {
39978         e.preventDefault();
39979         
39980         if(this.bgimage.length){
39981             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39982             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39983         }
39984     }
39985     
39986 });
39987
39988  
39989
39990  /*
39991  * - LGPL
39992  *
39993  * Number field 
39994  */
39995
39996 /**
39997  * @class Roo.bootstrap.form.NumberField
39998  * @extends Roo.bootstrap.form.Input
39999  * Bootstrap NumberField class
40000  * 
40001  * 
40002  * 
40003  * 
40004  * @constructor
40005  * Create a new NumberField
40006  * @param {Object} config The config object
40007  */
40008
40009 Roo.bootstrap.form.NumberField = function(config){
40010     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40011 };
40012
40013 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40014     
40015     /**
40016      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40017      */
40018     allowDecimals : true,
40019     /**
40020      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40021      */
40022     decimalSeparator : ".",
40023     /**
40024      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40025      */
40026     decimalPrecision : 2,
40027     /**
40028      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40029      */
40030     allowNegative : true,
40031     
40032     /**
40033      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40034      */
40035     allowZero: true,
40036     /**
40037      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40038      */
40039     minValue : Number.NEGATIVE_INFINITY,
40040     /**
40041      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40042      */
40043     maxValue : Number.MAX_VALUE,
40044     /**
40045      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40046      */
40047     minText : "The minimum value for this field is {0}",
40048     /**
40049      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40050      */
40051     maxText : "The maximum value for this field is {0}",
40052     /**
40053      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40054      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40055      */
40056     nanText : "{0} is not a valid number",
40057     /**
40058      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40059      */
40060     thousandsDelimiter : false,
40061     /**
40062      * @cfg {String} valueAlign alignment of value
40063      */
40064     valueAlign : "left",
40065
40066     getAutoCreate : function()
40067     {
40068         var hiddenInput = {
40069             tag: 'input',
40070             type: 'hidden',
40071             id: Roo.id(),
40072             cls: 'hidden-number-input'
40073         };
40074         
40075         if (this.name) {
40076             hiddenInput.name = this.name;
40077         }
40078         
40079         this.name = '';
40080         
40081         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40082         
40083         this.name = hiddenInput.name;
40084         
40085         if(cfg.cn.length > 0) {
40086             cfg.cn.push(hiddenInput);
40087         }
40088         
40089         return cfg;
40090     },
40091
40092     // private
40093     initEvents : function()
40094     {   
40095         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40096         
40097         var allowed = "0123456789";
40098         
40099         if(this.allowDecimals){
40100             allowed += this.decimalSeparator;
40101         }
40102         
40103         if(this.allowNegative){
40104             allowed += "-";
40105         }
40106         
40107         if(this.thousandsDelimiter) {
40108             allowed += ",";
40109         }
40110         
40111         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40112         
40113         var keyPress = function(e){
40114             
40115             var k = e.getKey();
40116             
40117             var c = e.getCharCode();
40118             
40119             if(
40120                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40121                     allowed.indexOf(String.fromCharCode(c)) === -1
40122             ){
40123                 e.stopEvent();
40124                 return;
40125             }
40126             
40127             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40128                 return;
40129             }
40130             
40131             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40132                 e.stopEvent();
40133             }
40134         };
40135         
40136         this.el.on("keypress", keyPress, this);
40137     },
40138     
40139     validateValue : function(value)
40140     {
40141         
40142         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40143             return false;
40144         }
40145         
40146         var num = this.parseValue(value);
40147         
40148         if(isNaN(num)){
40149             this.markInvalid(String.format(this.nanText, value));
40150             return false;
40151         }
40152         
40153         if(num < this.minValue){
40154             this.markInvalid(String.format(this.minText, this.minValue));
40155             return false;
40156         }
40157         
40158         if(num > this.maxValue){
40159             this.markInvalid(String.format(this.maxText, this.maxValue));
40160             return false;
40161         }
40162         
40163         return true;
40164     },
40165
40166     getValue : function()
40167     {
40168         var v = this.hiddenEl().getValue();
40169         
40170         return this.fixPrecision(this.parseValue(v));
40171     },
40172
40173     parseValue : function(value)
40174     {
40175         if(this.thousandsDelimiter) {
40176             value += "";
40177             r = new RegExp(",", "g");
40178             value = value.replace(r, "");
40179         }
40180         
40181         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40182         return isNaN(value) ? '' : value;
40183     },
40184
40185     fixPrecision : function(value)
40186     {
40187         if(this.thousandsDelimiter) {
40188             value += "";
40189             r = new RegExp(",", "g");
40190             value = value.replace(r, "");
40191         }
40192         
40193         var nan = isNaN(value);
40194         
40195         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40196             return nan ? '' : value;
40197         }
40198         return parseFloat(value).toFixed(this.decimalPrecision);
40199     },
40200
40201     setValue : function(v)
40202     {
40203         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40204         
40205         this.value = v;
40206         
40207         if(this.rendered){
40208             
40209             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40210             
40211             this.inputEl().dom.value = (v == '') ? '' :
40212                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40213             
40214             if(!this.allowZero && v === '0') {
40215                 this.hiddenEl().dom.value = '';
40216                 this.inputEl().dom.value = '';
40217             }
40218             
40219             this.validate();
40220         }
40221     },
40222
40223     decimalPrecisionFcn : function(v)
40224     {
40225         return Math.floor(v);
40226     },
40227
40228     beforeBlur : function()
40229     {
40230         var v = this.parseValue(this.getRawValue());
40231         
40232         if(v || v === 0 || v === ''){
40233             this.setValue(v);
40234         }
40235     },
40236     
40237     hiddenEl : function()
40238     {
40239         return this.el.select('input.hidden-number-input',true).first();
40240     }
40241     
40242 });
40243
40244  
40245
40246 /*
40247 * Licence: LGPL
40248 */
40249
40250 /**
40251  * @class Roo.bootstrap.DocumentSlider
40252  * @extends Roo.bootstrap.Component
40253  * Bootstrap DocumentSlider class
40254  * 
40255  * @constructor
40256  * Create a new DocumentViewer
40257  * @param {Object} config The config object
40258  */
40259
40260 Roo.bootstrap.DocumentSlider = function(config){
40261     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
40262     
40263     this.files = [];
40264     
40265     this.addEvents({
40266         /**
40267          * @event initial
40268          * Fire after initEvent
40269          * @param {Roo.bootstrap.DocumentSlider} this
40270          */
40271         "initial" : true,
40272         /**
40273          * @event update
40274          * Fire after update
40275          * @param {Roo.bootstrap.DocumentSlider} this
40276          */
40277         "update" : true,
40278         /**
40279          * @event click
40280          * Fire after click
40281          * @param {Roo.bootstrap.DocumentSlider} this
40282          */
40283         "click" : true
40284     });
40285 };
40286
40287 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
40288     
40289     files : false,
40290     
40291     indicator : 0,
40292     
40293     getAutoCreate : function()
40294     {
40295         var cfg = {
40296             tag : 'div',
40297             cls : 'roo-document-slider',
40298             cn : [
40299                 {
40300                     tag : 'div',
40301                     cls : 'roo-document-slider-header',
40302                     cn : [
40303                         {
40304                             tag : 'div',
40305                             cls : 'roo-document-slider-header-title'
40306                         }
40307                     ]
40308                 },
40309                 {
40310                     tag : 'div',
40311                     cls : 'roo-document-slider-body',
40312                     cn : [
40313                         {
40314                             tag : 'div',
40315                             cls : 'roo-document-slider-prev',
40316                             cn : [
40317                                 {
40318                                     tag : 'i',
40319                                     cls : 'fa fa-chevron-left'
40320                                 }
40321                             ]
40322                         },
40323                         {
40324                             tag : 'div',
40325                             cls : 'roo-document-slider-thumb',
40326                             cn : [
40327                                 {
40328                                     tag : 'img',
40329                                     cls : 'roo-document-slider-image'
40330                                 }
40331                             ]
40332                         },
40333                         {
40334                             tag : 'div',
40335                             cls : 'roo-document-slider-next',
40336                             cn : [
40337                                 {
40338                                     tag : 'i',
40339                                     cls : 'fa fa-chevron-right'
40340                                 }
40341                             ]
40342                         }
40343                     ]
40344                 }
40345             ]
40346         };
40347         
40348         return cfg;
40349     },
40350     
40351     initEvents : function()
40352     {
40353         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
40354         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
40355         
40356         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
40357         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
40358         
40359         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
40360         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
40361         
40362         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
40363         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
40364         
40365         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
40366         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
40367         
40368         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
40369         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
40370         
40371         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
40372         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
40373         
40374         this.thumbEl.on('click', this.onClick, this);
40375         
40376         this.prevIndicator.on('click', this.prev, this);
40377         
40378         this.nextIndicator.on('click', this.next, this);
40379         
40380     },
40381     
40382     initial : function()
40383     {
40384         if(this.files.length){
40385             this.indicator = 1;
40386             this.update()
40387         }
40388         
40389         this.fireEvent('initial', this);
40390     },
40391     
40392     update : function()
40393     {
40394         this.imageEl.attr('src', this.files[this.indicator - 1]);
40395         
40396         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
40397         
40398         this.prevIndicator.show();
40399         
40400         if(this.indicator == 1){
40401             this.prevIndicator.hide();
40402         }
40403         
40404         this.nextIndicator.show();
40405         
40406         if(this.indicator == this.files.length){
40407             this.nextIndicator.hide();
40408         }
40409         
40410         this.thumbEl.scrollTo('top');
40411         
40412         this.fireEvent('update', this);
40413     },
40414     
40415     onClick : function(e)
40416     {
40417         e.preventDefault();
40418         
40419         this.fireEvent('click', this);
40420     },
40421     
40422     prev : function(e)
40423     {
40424         e.preventDefault();
40425         
40426         this.indicator = Math.max(1, this.indicator - 1);
40427         
40428         this.update();
40429     },
40430     
40431     next : function(e)
40432     {
40433         e.preventDefault();
40434         
40435         this.indicator = Math.min(this.files.length, this.indicator + 1);
40436         
40437         this.update();
40438     }
40439 });
40440 /*
40441  * - LGPL
40442  *
40443  * RadioSet
40444  *
40445  *
40446  */
40447
40448 /**
40449  * @class Roo.bootstrap.form.RadioSet
40450  * @extends Roo.bootstrap.form.Input
40451  * @children Roo.bootstrap.form.Radio
40452  * Bootstrap RadioSet class
40453  * @cfg {String} indicatorpos (left|right) default left
40454  * @cfg {Boolean} inline (true|false) inline the element (default true)
40455  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
40456  * @constructor
40457  * Create a new RadioSet
40458  * @param {Object} config The config object
40459  */
40460
40461 Roo.bootstrap.form.RadioSet = function(config){
40462     
40463     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
40464     
40465     this.radioes = [];
40466     
40467     Roo.bootstrap.form.RadioSet.register(this);
40468     
40469     this.addEvents({
40470         /**
40471         * @event check
40472         * Fires when the element is checked or unchecked.
40473         * @param {Roo.bootstrap.form.RadioSet} this This radio
40474         * @param {Roo.bootstrap.form.Radio} item The checked item
40475         */
40476        check : true,
40477        /**
40478         * @event click
40479         * Fires when the element is click.
40480         * @param {Roo.bootstrap.form.RadioSet} this This radio set
40481         * @param {Roo.bootstrap.form.Radio} item The checked item
40482         * @param {Roo.EventObject} e The event object
40483         */
40484        click : true
40485     });
40486     
40487 };
40488
40489 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
40490
40491     radioes : false,
40492     
40493     inline : true,
40494     
40495     weight : '',
40496     
40497     indicatorpos : 'left',
40498     
40499     getAutoCreate : function()
40500     {
40501         var label = {
40502             tag : 'label',
40503             cls : 'roo-radio-set-label',
40504             cn : [
40505                 {
40506                     tag : 'span',
40507                     html : this.fieldLabel
40508                 }
40509             ]
40510         };
40511         if (Roo.bootstrap.version == 3) {
40512             
40513             
40514             if(this.indicatorpos == 'left'){
40515                 label.cn.unshift({
40516                     tag : 'i',
40517                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
40518                     tooltip : 'This field is required'
40519                 });
40520             } else {
40521                 label.cn.push({
40522                     tag : 'i',
40523                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
40524                     tooltip : 'This field is required'
40525                 });
40526             }
40527         }
40528         var items = {
40529             tag : 'div',
40530             cls : 'roo-radio-set-items'
40531         };
40532         
40533         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40534         
40535         if (align === 'left' && this.fieldLabel.length) {
40536             
40537             items = {
40538                 cls : "roo-radio-set-right", 
40539                 cn: [
40540                     items
40541                 ]
40542             };
40543             
40544             if(this.labelWidth > 12){
40545                 label.style = "width: " + this.labelWidth + 'px';
40546             }
40547             
40548             if(this.labelWidth < 13 && this.labelmd == 0){
40549                 this.labelmd = this.labelWidth;
40550             }
40551             
40552             if(this.labellg > 0){
40553                 label.cls += ' col-lg-' + this.labellg;
40554                 items.cls += ' col-lg-' + (12 - this.labellg);
40555             }
40556             
40557             if(this.labelmd > 0){
40558                 label.cls += ' col-md-' + this.labelmd;
40559                 items.cls += ' col-md-' + (12 - this.labelmd);
40560             }
40561             
40562             if(this.labelsm > 0){
40563                 label.cls += ' col-sm-' + this.labelsm;
40564                 items.cls += ' col-sm-' + (12 - this.labelsm);
40565             }
40566             
40567             if(this.labelxs > 0){
40568                 label.cls += ' col-xs-' + this.labelxs;
40569                 items.cls += ' col-xs-' + (12 - this.labelxs);
40570             }
40571         }
40572         
40573         var cfg = {
40574             tag : 'div',
40575             cls : 'roo-radio-set',
40576             cn : [
40577                 {
40578                     tag : 'input',
40579                     cls : 'roo-radio-set-input',
40580                     type : 'hidden',
40581                     name : this.name,
40582                     value : this.value ? this.value :  ''
40583                 },
40584                 label,
40585                 items
40586             ]
40587         };
40588         
40589         if(this.weight.length){
40590             cfg.cls += ' roo-radio-' + this.weight;
40591         }
40592         
40593         if(this.inline) {
40594             cfg.cls += ' roo-radio-set-inline';
40595         }
40596         
40597         var settings=this;
40598         ['xs','sm','md','lg'].map(function(size){
40599             if (settings[size]) {
40600                 cfg.cls += ' col-' + size + '-' + settings[size];
40601             }
40602         });
40603         
40604         return cfg;
40605         
40606     },
40607
40608     initEvents : function()
40609     {
40610         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40611         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40612         
40613         if(!this.fieldLabel.length){
40614             this.labelEl.hide();
40615         }
40616         
40617         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40618         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40619         
40620         this.indicator = this.indicatorEl();
40621         
40622         if(this.indicator){
40623             this.indicator.addClass('invisible');
40624         }
40625         
40626         this.originalValue = this.getValue();
40627         
40628     },
40629     
40630     inputEl: function ()
40631     {
40632         return this.el.select('.roo-radio-set-input', true).first();
40633     },
40634     
40635     getChildContainer : function()
40636     {
40637         return this.itemsEl;
40638     },
40639     
40640     register : function(item)
40641     {
40642         this.radioes.push(item);
40643         
40644     },
40645     
40646     validate : function()
40647     {   
40648         if(this.getVisibilityEl().hasClass('hidden')){
40649             return true;
40650         }
40651         
40652         var valid = false;
40653         
40654         Roo.each(this.radioes, function(i){
40655             if(!i.checked){
40656                 return;
40657             }
40658             
40659             valid = true;
40660             return false;
40661         });
40662         
40663         if(this.allowBlank) {
40664             return true;
40665         }
40666         
40667         if(this.disabled || valid){
40668             this.markValid();
40669             return true;
40670         }
40671         
40672         this.markInvalid();
40673         return false;
40674         
40675     },
40676     
40677     markValid : function()
40678     {
40679         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40680             this.indicatorEl().removeClass('visible');
40681             this.indicatorEl().addClass('invisible');
40682         }
40683         
40684         
40685         if (Roo.bootstrap.version == 3) {
40686             this.el.removeClass([this.invalidClass, this.validClass]);
40687             this.el.addClass(this.validClass);
40688         } else {
40689             this.el.removeClass(['is-invalid','is-valid']);
40690             this.el.addClass(['is-valid']);
40691         }
40692         this.fireEvent('valid', this);
40693     },
40694     
40695     markInvalid : function(msg)
40696     {
40697         if(this.allowBlank || this.disabled){
40698             return;
40699         }
40700         
40701         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40702             this.indicatorEl().removeClass('invisible');
40703             this.indicatorEl().addClass('visible');
40704         }
40705         if (Roo.bootstrap.version == 3) {
40706             this.el.removeClass([this.invalidClass, this.validClass]);
40707             this.el.addClass(this.invalidClass);
40708         } else {
40709             this.el.removeClass(['is-invalid','is-valid']);
40710             this.el.addClass(['is-invalid']);
40711         }
40712         
40713         this.fireEvent('invalid', this, msg);
40714         
40715     },
40716     
40717     setValue : function(v, suppressEvent)
40718     {   
40719         if(this.value === v){
40720             return;
40721         }
40722         
40723         this.value = v;
40724         
40725         if(this.rendered){
40726             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40727         }
40728         
40729         Roo.each(this.radioes, function(i){
40730             i.checked = false;
40731             i.el.removeClass('checked');
40732         });
40733         
40734         Roo.each(this.radioes, function(i){
40735             
40736             if(i.value === v || i.value.toString() === v.toString()){
40737                 i.checked = true;
40738                 i.el.addClass('checked');
40739                 
40740                 if(suppressEvent !== true){
40741                     this.fireEvent('check', this, i);
40742                 }
40743                 
40744                 return false;
40745             }
40746             
40747         }, this);
40748         
40749         this.validate();
40750     },
40751     
40752     clearInvalid : function(){
40753         
40754         if(!this.el || this.preventMark){
40755             return;
40756         }
40757         
40758         this.el.removeClass([this.invalidClass]);
40759         
40760         this.fireEvent('valid', this);
40761     }
40762     
40763 });
40764
40765 Roo.apply(Roo.bootstrap.form.RadioSet, {
40766     
40767     groups: {},
40768     
40769     register : function(set)
40770     {
40771         this.groups[set.name] = set;
40772     },
40773     
40774     get: function(name) 
40775     {
40776         if (typeof(this.groups[name]) == 'undefined') {
40777             return false;
40778         }
40779         
40780         return this.groups[name] ;
40781     }
40782     
40783 });
40784 /*
40785  * Based on:
40786  * Ext JS Library 1.1.1
40787  * Copyright(c) 2006-2007, Ext JS, LLC.
40788  *
40789  * Originally Released Under LGPL - original licence link has changed is not relivant.
40790  *
40791  * Fork - LGPL
40792  * <script type="text/javascript">
40793  */
40794
40795
40796 /**
40797  * @class Roo.bootstrap.SplitBar
40798  * @extends Roo.util.Observable
40799  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40800  * <br><br>
40801  * Usage:
40802  * <pre><code>
40803 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40804                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40805 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40806 split.minSize = 100;
40807 split.maxSize = 600;
40808 split.animate = true;
40809 split.on('moved', splitterMoved);
40810 </code></pre>
40811  * @constructor
40812  * Create a new SplitBar
40813  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40814  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40815  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40816  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40817                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40818                         position of the SplitBar).
40819  */
40820 Roo.bootstrap.SplitBar = function(cfg){
40821     
40822     /** @private */
40823     
40824     //{
40825     //  dragElement : elm
40826     //  resizingElement: el,
40827         // optional..
40828     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40829     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40830         // existingProxy ???
40831     //}
40832     
40833     this.el = Roo.get(cfg.dragElement, true);
40834     this.el.dom.unselectable = "on";
40835     /** @private */
40836     this.resizingEl = Roo.get(cfg.resizingElement, true);
40837
40838     /**
40839      * @private
40840      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40841      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40842      * @type Number
40843      */
40844     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40845     
40846     /**
40847      * The minimum size of the resizing element. (Defaults to 0)
40848      * @type Number
40849      */
40850     this.minSize = 0;
40851     
40852     /**
40853      * The maximum size of the resizing element. (Defaults to 2000)
40854      * @type Number
40855      */
40856     this.maxSize = 2000;
40857     
40858     /**
40859      * Whether to animate the transition to the new size
40860      * @type Boolean
40861      */
40862     this.animate = false;
40863     
40864     /**
40865      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40866      * @type Boolean
40867      */
40868     this.useShim = false;
40869     
40870     /** @private */
40871     this.shim = null;
40872     
40873     if(!cfg.existingProxy){
40874         /** @private */
40875         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40876     }else{
40877         this.proxy = Roo.get(cfg.existingProxy).dom;
40878     }
40879     /** @private */
40880     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40881     
40882     /** @private */
40883     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40884     
40885     /** @private */
40886     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40887     
40888     /** @private */
40889     this.dragSpecs = {};
40890     
40891     /**
40892      * @private The adapter to use to positon and resize elements
40893      */
40894     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40895     this.adapter.init(this);
40896     
40897     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40898         /** @private */
40899         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40900         this.el.addClass("roo-splitbar-h");
40901     }else{
40902         /** @private */
40903         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40904         this.el.addClass("roo-splitbar-v");
40905     }
40906     
40907     this.addEvents({
40908         /**
40909          * @event resize
40910          * Fires when the splitter is moved (alias for {@link #event-moved})
40911          * @param {Roo.bootstrap.SplitBar} this
40912          * @param {Number} newSize the new width or height
40913          */
40914         "resize" : true,
40915         /**
40916          * @event moved
40917          * Fires when the splitter is moved
40918          * @param {Roo.bootstrap.SplitBar} this
40919          * @param {Number} newSize the new width or height
40920          */
40921         "moved" : true,
40922         /**
40923          * @event beforeresize
40924          * Fires before the splitter is dragged
40925          * @param {Roo.bootstrap.SplitBar} this
40926          */
40927         "beforeresize" : true,
40928
40929         "beforeapply" : true
40930     });
40931
40932     Roo.util.Observable.call(this);
40933 };
40934
40935 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40936     onStartProxyDrag : function(x, y){
40937         this.fireEvent("beforeresize", this);
40938         if(!this.overlay){
40939             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40940             o.unselectable();
40941             o.enableDisplayMode("block");
40942             // all splitbars share the same overlay
40943             Roo.bootstrap.SplitBar.prototype.overlay = o;
40944         }
40945         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40946         this.overlay.show();
40947         Roo.get(this.proxy).setDisplayed("block");
40948         var size = this.adapter.getElementSize(this);
40949         this.activeMinSize = this.getMinimumSize();;
40950         this.activeMaxSize = this.getMaximumSize();;
40951         var c1 = size - this.activeMinSize;
40952         var c2 = Math.max(this.activeMaxSize - size, 0);
40953         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40954             this.dd.resetConstraints();
40955             this.dd.setXConstraint(
40956                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40957                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40958             );
40959             this.dd.setYConstraint(0, 0);
40960         }else{
40961             this.dd.resetConstraints();
40962             this.dd.setXConstraint(0, 0);
40963             this.dd.setYConstraint(
40964                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40965                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40966             );
40967          }
40968         this.dragSpecs.startSize = size;
40969         this.dragSpecs.startPoint = [x, y];
40970         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40971     },
40972     
40973     /** 
40974      * @private Called after the drag operation by the DDProxy
40975      */
40976     onEndProxyDrag : function(e){
40977         Roo.get(this.proxy).setDisplayed(false);
40978         var endPoint = Roo.lib.Event.getXY(e);
40979         if(this.overlay){
40980             this.overlay.hide();
40981         }
40982         var newSize;
40983         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40984             newSize = this.dragSpecs.startSize + 
40985                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40986                     endPoint[0] - this.dragSpecs.startPoint[0] :
40987                     this.dragSpecs.startPoint[0] - endPoint[0]
40988                 );
40989         }else{
40990             newSize = this.dragSpecs.startSize + 
40991                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40992                     endPoint[1] - this.dragSpecs.startPoint[1] :
40993                     this.dragSpecs.startPoint[1] - endPoint[1]
40994                 );
40995         }
40996         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40997         if(newSize != this.dragSpecs.startSize){
40998             if(this.fireEvent('beforeapply', this, newSize) !== false){
40999                 this.adapter.setElementSize(this, newSize);
41000                 this.fireEvent("moved", this, newSize);
41001                 this.fireEvent("resize", this, newSize);
41002             }
41003         }
41004     },
41005     
41006     /**
41007      * Get the adapter this SplitBar uses
41008      * @return The adapter object
41009      */
41010     getAdapter : function(){
41011         return this.adapter;
41012     },
41013     
41014     /**
41015      * Set the adapter this SplitBar uses
41016      * @param {Object} adapter A SplitBar adapter object
41017      */
41018     setAdapter : function(adapter){
41019         this.adapter = adapter;
41020         this.adapter.init(this);
41021     },
41022     
41023     /**
41024      * Gets the minimum size for the resizing element
41025      * @return {Number} The minimum size
41026      */
41027     getMinimumSize : function(){
41028         return this.minSize;
41029     },
41030     
41031     /**
41032      * Sets the minimum size for the resizing element
41033      * @param {Number} minSize The minimum size
41034      */
41035     setMinimumSize : function(minSize){
41036         this.minSize = minSize;
41037     },
41038     
41039     /**
41040      * Gets the maximum size for the resizing element
41041      * @return {Number} The maximum size
41042      */
41043     getMaximumSize : function(){
41044         return this.maxSize;
41045     },
41046     
41047     /**
41048      * Sets the maximum size for the resizing element
41049      * @param {Number} maxSize The maximum size
41050      */
41051     setMaximumSize : function(maxSize){
41052         this.maxSize = maxSize;
41053     },
41054     
41055     /**
41056      * Sets the initialize size for the resizing element
41057      * @param {Number} size The initial size
41058      */
41059     setCurrentSize : function(size){
41060         var oldAnimate = this.animate;
41061         this.animate = false;
41062         this.adapter.setElementSize(this, size);
41063         this.animate = oldAnimate;
41064     },
41065     
41066     /**
41067      * Destroy this splitbar. 
41068      * @param {Boolean} removeEl True to remove the element
41069      */
41070     destroy : function(removeEl){
41071         if(this.shim){
41072             this.shim.remove();
41073         }
41074         this.dd.unreg();
41075         this.proxy.parentNode.removeChild(this.proxy);
41076         if(removeEl){
41077             this.el.remove();
41078         }
41079     }
41080 });
41081
41082 /**
41083  * @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.
41084  */
41085 Roo.bootstrap.SplitBar.createProxy = function(dir){
41086     var proxy = new Roo.Element(document.createElement("div"));
41087     proxy.unselectable();
41088     var cls = 'roo-splitbar-proxy';
41089     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41090     document.body.appendChild(proxy.dom);
41091     return proxy.dom;
41092 };
41093
41094 /** 
41095  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41096  * Default Adapter. It assumes the splitter and resizing element are not positioned
41097  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41098  */
41099 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41100 };
41101
41102 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41103     // do nothing for now
41104     init : function(s){
41105     
41106     },
41107     /**
41108      * Called before drag operations to get the current size of the resizing element. 
41109      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41110      */
41111      getElementSize : function(s){
41112         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41113             return s.resizingEl.getWidth();
41114         }else{
41115             return s.resizingEl.getHeight();
41116         }
41117     },
41118     
41119     /**
41120      * Called after drag operations to set the size of the resizing element.
41121      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41122      * @param {Number} newSize The new size to set
41123      * @param {Function} onComplete A function to be invoked when resizing is complete
41124      */
41125     setElementSize : function(s, newSize, onComplete){
41126         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41127             if(!s.animate){
41128                 s.resizingEl.setWidth(newSize);
41129                 if(onComplete){
41130                     onComplete(s, newSize);
41131                 }
41132             }else{
41133                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41134             }
41135         }else{
41136             
41137             if(!s.animate){
41138                 s.resizingEl.setHeight(newSize);
41139                 if(onComplete){
41140                     onComplete(s, newSize);
41141                 }
41142             }else{
41143                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41144             }
41145         }
41146     }
41147 };
41148
41149 /** 
41150  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41151  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41152  * Adapter that  moves the splitter element to align with the resized sizing element. 
41153  * Used with an absolute positioned SplitBar.
41154  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41155  * document.body, make sure you assign an id to the body element.
41156  */
41157 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41158     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41159     this.container = Roo.get(container);
41160 };
41161
41162 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41163     init : function(s){
41164         this.basic.init(s);
41165     },
41166     
41167     getElementSize : function(s){
41168         return this.basic.getElementSize(s);
41169     },
41170     
41171     setElementSize : function(s, newSize, onComplete){
41172         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41173     },
41174     
41175     moveSplitter : function(s){
41176         var yes = Roo.bootstrap.SplitBar;
41177         switch(s.placement){
41178             case yes.LEFT:
41179                 s.el.setX(s.resizingEl.getRight());
41180                 break;
41181             case yes.RIGHT:
41182                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41183                 break;
41184             case yes.TOP:
41185                 s.el.setY(s.resizingEl.getBottom());
41186                 break;
41187             case yes.BOTTOM:
41188                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41189                 break;
41190         }
41191     }
41192 };
41193
41194 /**
41195  * Orientation constant - Create a vertical SplitBar
41196  * @static
41197  * @type Number
41198  */
41199 Roo.bootstrap.SplitBar.VERTICAL = 1;
41200
41201 /**
41202  * Orientation constant - Create a horizontal SplitBar
41203  * @static
41204  * @type Number
41205  */
41206 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
41207
41208 /**
41209  * Placement constant - The resizing element is to the left of the splitter element
41210  * @static
41211  * @type Number
41212  */
41213 Roo.bootstrap.SplitBar.LEFT = 1;
41214
41215 /**
41216  * Placement constant - The resizing element is to the right of the splitter element
41217  * @static
41218  * @type Number
41219  */
41220 Roo.bootstrap.SplitBar.RIGHT = 2;
41221
41222 /**
41223  * Placement constant - The resizing element is positioned above the splitter element
41224  * @static
41225  * @type Number
41226  */
41227 Roo.bootstrap.SplitBar.TOP = 3;
41228
41229 /**
41230  * Placement constant - The resizing element is positioned under splitter element
41231  * @static
41232  * @type Number
41233  */
41234 Roo.bootstrap.SplitBar.BOTTOM = 4;
41235 /*
41236  * Based on:
41237  * Ext JS Library 1.1.1
41238  * Copyright(c) 2006-2007, Ext JS, LLC.
41239  *
41240  * Originally Released Under LGPL - original licence link has changed is not relivant.
41241  *
41242  * Fork - LGPL
41243  * <script type="text/javascript">
41244  */
41245
41246 /**
41247  * @class Roo.bootstrap.layout.Manager
41248  * @extends Roo.bootstrap.Component
41249  * @abstract
41250  * Base class for layout managers.
41251  */
41252 Roo.bootstrap.layout.Manager = function(config)
41253 {
41254     this.monitorWindowResize = true; // do this before we apply configuration.
41255     
41256     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
41257
41258
41259
41260
41261
41262     /** false to disable window resize monitoring @type Boolean */
41263     
41264     this.regions = {};
41265     this.addEvents({
41266         /**
41267          * @event layout
41268          * Fires when a layout is performed.
41269          * @param {Roo.LayoutManager} this
41270          */
41271         "layout" : true,
41272         /**
41273          * @event regionresized
41274          * Fires when the user resizes a region.
41275          * @param {Roo.LayoutRegion} region The resized region
41276          * @param {Number} newSize The new size (width for east/west, height for north/south)
41277          */
41278         "regionresized" : true,
41279         /**
41280          * @event regioncollapsed
41281          * Fires when a region is collapsed.
41282          * @param {Roo.LayoutRegion} region The collapsed region
41283          */
41284         "regioncollapsed" : true,
41285         /**
41286          * @event regionexpanded
41287          * Fires when a region is expanded.
41288          * @param {Roo.LayoutRegion} region The expanded region
41289          */
41290         "regionexpanded" : true
41291     });
41292     this.updating = false;
41293
41294     if (config.el) {
41295         this.el = Roo.get(config.el);
41296         this.initEvents();
41297     }
41298
41299 };
41300
41301 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
41302
41303
41304     regions : null,
41305
41306     monitorWindowResize : true,
41307
41308
41309     updating : false,
41310
41311
41312     onRender : function(ct, position)
41313     {
41314         if(!this.el){
41315             this.el = Roo.get(ct);
41316             this.initEvents();
41317         }
41318         //this.fireEvent('render',this);
41319     },
41320
41321
41322     initEvents: function()
41323     {
41324
41325
41326         // ie scrollbar fix
41327         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
41328             document.body.scroll = "no";
41329         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
41330             this.el.position('relative');
41331         }
41332         this.id = this.el.id;
41333         this.el.addClass("roo-layout-container");
41334         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
41335         if(this.el.dom != document.body ) {
41336             this.el.on('resize', this.layout,this);
41337             this.el.on('show', this.layout,this);
41338         }
41339
41340     },
41341
41342     /**
41343      * Returns true if this layout is currently being updated
41344      * @return {Boolean}
41345      */
41346     isUpdating : function(){
41347         return this.updating;
41348     },
41349
41350     /**
41351      * Suspend the LayoutManager from doing auto-layouts while
41352      * making multiple add or remove calls
41353      */
41354     beginUpdate : function(){
41355         this.updating = true;
41356     },
41357
41358     /**
41359      * Restore auto-layouts and optionally disable the manager from performing a layout
41360      * @param {Boolean} noLayout true to disable a layout update
41361      */
41362     endUpdate : function(noLayout){
41363         this.updating = false;
41364         if(!noLayout){
41365             this.layout();
41366         }
41367     },
41368
41369     layout: function(){
41370         // abstract...
41371     },
41372
41373     onRegionResized : function(region, newSize){
41374         this.fireEvent("regionresized", region, newSize);
41375         this.layout();
41376     },
41377
41378     onRegionCollapsed : function(region){
41379         this.fireEvent("regioncollapsed", region);
41380     },
41381
41382     onRegionExpanded : function(region){
41383         this.fireEvent("regionexpanded", region);
41384     },
41385
41386     /**
41387      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
41388      * performs box-model adjustments.
41389      * @return {Object} The size as an object {width: (the width), height: (the height)}
41390      */
41391     getViewSize : function()
41392     {
41393         var size;
41394         if(this.el.dom != document.body){
41395             size = this.el.getSize();
41396         }else{
41397             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
41398         }
41399         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
41400         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41401         return size;
41402     },
41403
41404     /**
41405      * Returns the Element this layout is bound to.
41406      * @return {Roo.Element}
41407      */
41408     getEl : function(){
41409         return this.el;
41410     },
41411
41412     /**
41413      * Returns the specified region.
41414      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
41415      * @return {Roo.LayoutRegion}
41416      */
41417     getRegion : function(target){
41418         return this.regions[target.toLowerCase()];
41419     },
41420
41421     onWindowResize : function(){
41422         if(this.monitorWindowResize){
41423             this.layout();
41424         }
41425     }
41426 });
41427 /*
41428  * Based on:
41429  * Ext JS Library 1.1.1
41430  * Copyright(c) 2006-2007, Ext JS, LLC.
41431  *
41432  * Originally Released Under LGPL - original licence link has changed is not relivant.
41433  *
41434  * Fork - LGPL
41435  * <script type="text/javascript">
41436  */
41437 /**
41438  * @class Roo.bootstrap.layout.Border
41439  * @extends Roo.bootstrap.layout.Manager
41440  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
41441  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
41442  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
41443  * please see: examples/bootstrap/nested.html<br><br>
41444  
41445 <b>The container the layout is rendered into can be either the body element or any other element.
41446 If it is not the body element, the container needs to either be an absolute positioned element,
41447 or you will need to add "position:relative" to the css of the container.  You will also need to specify
41448 the container size if it is not the body element.</b>
41449
41450 * @constructor
41451 * Create a new Border
41452 * @param {Object} config Configuration options
41453  */
41454 Roo.bootstrap.layout.Border = function(config){
41455     config = config || {};
41456     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
41457     
41458     
41459     
41460     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41461         if(config[region]){
41462             config[region].region = region;
41463             this.addRegion(config[region]);
41464         }
41465     },this);
41466     
41467 };
41468
41469 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
41470
41471 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
41472     
41473         /**
41474          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
41475          */
41476         /**
41477          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
41478          */
41479         /**
41480          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
41481          */
41482         /**
41483          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
41484          */
41485         /**
41486          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
41487          */
41488         
41489         
41490         
41491         
41492     parent : false, // this might point to a 'nest' or a ???
41493     
41494     /**
41495      * Creates and adds a new region if it doesn't already exist.
41496      * @param {String} target The target region key (north, south, east, west or center).
41497      * @param {Object} config The regions config object
41498      * @return {BorderLayoutRegion} The new region
41499      */
41500     addRegion : function(config)
41501     {
41502         if(!this.regions[config.region]){
41503             var r = this.factory(config);
41504             this.bindRegion(r);
41505         }
41506         return this.regions[config.region];
41507     },
41508
41509     // private (kinda)
41510     bindRegion : function(r){
41511         this.regions[r.config.region] = r;
41512         
41513         r.on("visibilitychange",    this.layout, this);
41514         r.on("paneladded",          this.layout, this);
41515         r.on("panelremoved",        this.layout, this);
41516         r.on("invalidated",         this.layout, this);
41517         r.on("resized",             this.onRegionResized, this);
41518         r.on("collapsed",           this.onRegionCollapsed, this);
41519         r.on("expanded",            this.onRegionExpanded, this);
41520     },
41521
41522     /**
41523      * Performs a layout update.
41524      */
41525     layout : function()
41526     {
41527         if(this.updating) {
41528             return;
41529         }
41530         
41531         // render all the rebions if they have not been done alreayd?
41532         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41533             if(this.regions[region] && !this.regions[region].bodyEl){
41534                 this.regions[region].onRender(this.el)
41535             }
41536         },this);
41537         
41538         var size = this.getViewSize();
41539         var w = size.width;
41540         var h = size.height;
41541         var centerW = w;
41542         var centerH = h;
41543         var centerY = 0;
41544         var centerX = 0;
41545         //var x = 0, y = 0;
41546
41547         var rs = this.regions;
41548         var north = rs["north"];
41549         var south = rs["south"]; 
41550         var west = rs["west"];
41551         var east = rs["east"];
41552         var center = rs["center"];
41553         //if(this.hideOnLayout){ // not supported anymore
41554             //c.el.setStyle("display", "none");
41555         //}
41556         if(north && north.isVisible()){
41557             var b = north.getBox();
41558             var m = north.getMargins();
41559             b.width = w - (m.left+m.right);
41560             b.x = m.left;
41561             b.y = m.top;
41562             centerY = b.height + b.y + m.bottom;
41563             centerH -= centerY;
41564             north.updateBox(this.safeBox(b));
41565         }
41566         if(south && south.isVisible()){
41567             var b = south.getBox();
41568             var m = south.getMargins();
41569             b.width = w - (m.left+m.right);
41570             b.x = m.left;
41571             var totalHeight = (b.height + m.top + m.bottom);
41572             b.y = h - totalHeight + m.top;
41573             centerH -= totalHeight;
41574             south.updateBox(this.safeBox(b));
41575         }
41576         if(west && west.isVisible()){
41577             var b = west.getBox();
41578             var m = west.getMargins();
41579             b.height = centerH - (m.top+m.bottom);
41580             b.x = m.left;
41581             b.y = centerY + m.top;
41582             var totalWidth = (b.width + m.left + m.right);
41583             centerX += totalWidth;
41584             centerW -= totalWidth;
41585             west.updateBox(this.safeBox(b));
41586         }
41587         if(east && east.isVisible()){
41588             var b = east.getBox();
41589             var m = east.getMargins();
41590             b.height = centerH - (m.top+m.bottom);
41591             var totalWidth = (b.width + m.left + m.right);
41592             b.x = w - totalWidth + m.left;
41593             b.y = centerY + m.top;
41594             centerW -= totalWidth;
41595             east.updateBox(this.safeBox(b));
41596         }
41597         if(center){
41598             var m = center.getMargins();
41599             var centerBox = {
41600                 x: centerX + m.left,
41601                 y: centerY + m.top,
41602                 width: centerW - (m.left+m.right),
41603                 height: centerH - (m.top+m.bottom)
41604             };
41605             //if(this.hideOnLayout){
41606                 //center.el.setStyle("display", "block");
41607             //}
41608             center.updateBox(this.safeBox(centerBox));
41609         }
41610         this.el.repaint();
41611         this.fireEvent("layout", this);
41612     },
41613
41614     // private
41615     safeBox : function(box){
41616         box.width = Math.max(0, box.width);
41617         box.height = Math.max(0, box.height);
41618         return box;
41619     },
41620
41621     /**
41622      * Adds a ContentPanel (or subclass) to this layout.
41623      * @param {String} target The target region key (north, south, east, west or center).
41624      * @param {Roo.ContentPanel} panel The panel to add
41625      * @return {Roo.ContentPanel} The added panel
41626      */
41627     add : function(target, panel){
41628          
41629         target = target.toLowerCase();
41630         return this.regions[target].add(panel);
41631     },
41632
41633     /**
41634      * Remove a ContentPanel (or subclass) to this layout.
41635      * @param {String} target The target region key (north, south, east, west or center).
41636      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41637      * @return {Roo.ContentPanel} The removed panel
41638      */
41639     remove : function(target, panel){
41640         target = target.toLowerCase();
41641         return this.regions[target].remove(panel);
41642     },
41643
41644     /**
41645      * Searches all regions for a panel with the specified id
41646      * @param {String} panelId
41647      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41648      */
41649     findPanel : function(panelId){
41650         var rs = this.regions;
41651         for(var target in rs){
41652             if(typeof rs[target] != "function"){
41653                 var p = rs[target].getPanel(panelId);
41654                 if(p){
41655                     return p;
41656                 }
41657             }
41658         }
41659         return null;
41660     },
41661
41662     /**
41663      * Searches all regions for a panel with the specified id and activates (shows) it.
41664      * @param {String/ContentPanel} panelId The panels id or the panel itself
41665      * @return {Roo.ContentPanel} The shown panel or null
41666      */
41667     showPanel : function(panelId) {
41668       var rs = this.regions;
41669       for(var target in rs){
41670          var r = rs[target];
41671          if(typeof r != "function"){
41672             if(r.hasPanel(panelId)){
41673                return r.showPanel(panelId);
41674             }
41675          }
41676       }
41677       return null;
41678    },
41679
41680    /**
41681      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41682      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41683      */
41684    /*
41685     restoreState : function(provider){
41686         if(!provider){
41687             provider = Roo.state.Manager;
41688         }
41689         var sm = new Roo.LayoutStateManager();
41690         sm.init(this, provider);
41691     },
41692 */
41693  
41694  
41695     /**
41696      * Adds a xtype elements to the layout.
41697      * <pre><code>
41698
41699 layout.addxtype({
41700        xtype : 'ContentPanel',
41701        region: 'west',
41702        items: [ .... ]
41703    }
41704 );
41705
41706 layout.addxtype({
41707         xtype : 'NestedLayoutPanel',
41708         region: 'west',
41709         layout: {
41710            center: { },
41711            west: { }   
41712         },
41713         items : [ ... list of content panels or nested layout panels.. ]
41714    }
41715 );
41716 </code></pre>
41717      * @param {Object} cfg Xtype definition of item to add.
41718      */
41719     addxtype : function(cfg)
41720     {
41721         // basically accepts a pannel...
41722         // can accept a layout region..!?!?
41723         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41724         
41725         
41726         // theory?  children can only be panels??
41727         
41728         //if (!cfg.xtype.match(/Panel$/)) {
41729         //    return false;
41730         //}
41731         var ret = false;
41732         
41733         if (typeof(cfg.region) == 'undefined') {
41734             Roo.log("Failed to add Panel, region was not set");
41735             Roo.log(cfg);
41736             return false;
41737         }
41738         var region = cfg.region;
41739         delete cfg.region;
41740         
41741           
41742         var xitems = [];
41743         if (cfg.items) {
41744             xitems = cfg.items;
41745             delete cfg.items;
41746         }
41747         var nb = false;
41748         
41749         if ( region == 'center') {
41750             Roo.log("Center: " + cfg.title);
41751         }
41752         
41753         
41754         switch(cfg.xtype) 
41755         {
41756             case 'Content':  // ContentPanel (el, cfg)
41757             case 'Scroll':  // ContentPanel (el, cfg)
41758             case 'View': 
41759                 cfg.autoCreate = cfg.autoCreate || true;
41760                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41761                 //} else {
41762                 //    var el = this.el.createChild();
41763                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41764                 //}
41765                 
41766                 this.add(region, ret);
41767                 break;
41768             
41769             /*
41770             case 'TreePanel': // our new panel!
41771                 cfg.el = this.el.createChild();
41772                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41773                 this.add(region, ret);
41774                 break;
41775             */
41776             
41777             case 'Nest': 
41778                 // create a new Layout (which is  a Border Layout...
41779                 
41780                 var clayout = cfg.layout;
41781                 clayout.el  = this.el.createChild();
41782                 clayout.items   = clayout.items  || [];
41783                 
41784                 delete cfg.layout;
41785                 
41786                 // replace this exitems with the clayout ones..
41787                 xitems = clayout.items;
41788                  
41789                 // force background off if it's in center...
41790                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41791                     cfg.background = false;
41792                 }
41793                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41794                 
41795                 
41796                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41797                 //console.log('adding nested layout panel '  + cfg.toSource());
41798                 this.add(region, ret);
41799                 nb = {}; /// find first...
41800                 break;
41801             
41802             case 'Grid':
41803                 
41804                 // needs grid and region
41805                 
41806                 //var el = this.getRegion(region).el.createChild();
41807                 /*
41808                  *var el = this.el.createChild();
41809                 // create the grid first...
41810                 cfg.grid.container = el;
41811                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41812                 */
41813                 
41814                 if (region == 'center' && this.active ) {
41815                     cfg.background = false;
41816                 }
41817                 
41818                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41819                 
41820                 this.add(region, ret);
41821                 /*
41822                 if (cfg.background) {
41823                     // render grid on panel activation (if panel background)
41824                     ret.on('activate', function(gp) {
41825                         if (!gp.grid.rendered) {
41826                     //        gp.grid.render(el);
41827                         }
41828                     });
41829                 } else {
41830                   //  cfg.grid.render(el);
41831                 }
41832                 */
41833                 break;
41834            
41835            
41836             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41837                 // it was the old xcomponent building that caused this before.
41838                 // espeically if border is the top element in the tree.
41839                 ret = this;
41840                 break; 
41841                 
41842                     
41843                 
41844                 
41845                 
41846             default:
41847                 /*
41848                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41849                     
41850                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41851                     this.add(region, ret);
41852                 } else {
41853                 */
41854                     Roo.log(cfg);
41855                     throw "Can not add '" + cfg.xtype + "' to Border";
41856                     return null;
41857              
41858                                 
41859              
41860         }
41861         this.beginUpdate();
41862         // add children..
41863         var region = '';
41864         var abn = {};
41865         Roo.each(xitems, function(i)  {
41866             region = nb && i.region ? i.region : false;
41867             
41868             var add = ret.addxtype(i);
41869            
41870             if (region) {
41871                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41872                 if (!i.background) {
41873                     abn[region] = nb[region] ;
41874                 }
41875             }
41876             
41877         });
41878         this.endUpdate();
41879
41880         // make the last non-background panel active..
41881         //if (nb) { Roo.log(abn); }
41882         if (nb) {
41883             
41884             for(var r in abn) {
41885                 region = this.getRegion(r);
41886                 if (region) {
41887                     // tried using nb[r], but it does not work..
41888                      
41889                     region.showPanel(abn[r]);
41890                    
41891                 }
41892             }
41893         }
41894         return ret;
41895         
41896     },
41897     
41898     
41899 // private
41900     factory : function(cfg)
41901     {
41902         
41903         var validRegions = Roo.bootstrap.layout.Border.regions;
41904
41905         var target = cfg.region;
41906         cfg.mgr = this;
41907         
41908         var r = Roo.bootstrap.layout;
41909         Roo.log(target);
41910         switch(target){
41911             case "north":
41912                 return new r.North(cfg);
41913             case "south":
41914                 return new r.South(cfg);
41915             case "east":
41916                 return new r.East(cfg);
41917             case "west":
41918                 return new r.West(cfg);
41919             case "center":
41920                 return new r.Center(cfg);
41921         }
41922         throw 'Layout region "'+target+'" not supported.';
41923     }
41924     
41925     
41926 });
41927  /*
41928  * Based on:
41929  * Ext JS Library 1.1.1
41930  * Copyright(c) 2006-2007, Ext JS, LLC.
41931  *
41932  * Originally Released Under LGPL - original licence link has changed is not relivant.
41933  *
41934  * Fork - LGPL
41935  * <script type="text/javascript">
41936  */
41937  
41938 /**
41939  * @class Roo.bootstrap.layout.Basic
41940  * @extends Roo.util.Observable
41941  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41942  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41943  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41944  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41945  * @cfg {string}   region  the region that it inhabits..
41946  * @cfg {bool}   skipConfig skip config?
41947  * 
41948
41949  */
41950 Roo.bootstrap.layout.Basic = function(config){
41951     
41952     this.mgr = config.mgr;
41953     
41954     this.position = config.region;
41955     
41956     var skipConfig = config.skipConfig;
41957     
41958     this.events = {
41959         /**
41960          * @scope Roo.BasicLayoutRegion
41961          */
41962         
41963         /**
41964          * @event beforeremove
41965          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41966          * @param {Roo.LayoutRegion} this
41967          * @param {Roo.ContentPanel} panel The panel
41968          * @param {Object} e The cancel event object
41969          */
41970         "beforeremove" : true,
41971         /**
41972          * @event invalidated
41973          * Fires when the layout for this region is changed.
41974          * @param {Roo.LayoutRegion} this
41975          */
41976         "invalidated" : true,
41977         /**
41978          * @event visibilitychange
41979          * Fires when this region is shown or hidden 
41980          * @param {Roo.LayoutRegion} this
41981          * @param {Boolean} visibility true or false
41982          */
41983         "visibilitychange" : true,
41984         /**
41985          * @event paneladded
41986          * Fires when a panel is added. 
41987          * @param {Roo.LayoutRegion} this
41988          * @param {Roo.ContentPanel} panel The panel
41989          */
41990         "paneladded" : true,
41991         /**
41992          * @event panelremoved
41993          * Fires when a panel is removed. 
41994          * @param {Roo.LayoutRegion} this
41995          * @param {Roo.ContentPanel} panel The panel
41996          */
41997         "panelremoved" : true,
41998         /**
41999          * @event beforecollapse
42000          * Fires when this region before collapse.
42001          * @param {Roo.LayoutRegion} this
42002          */
42003         "beforecollapse" : true,
42004         /**
42005          * @event collapsed
42006          * Fires when this region is collapsed.
42007          * @param {Roo.LayoutRegion} this
42008          */
42009         "collapsed" : true,
42010         /**
42011          * @event expanded
42012          * Fires when this region is expanded.
42013          * @param {Roo.LayoutRegion} this
42014          */
42015         "expanded" : true,
42016         /**
42017          * @event slideshow
42018          * Fires when this region is slid into view.
42019          * @param {Roo.LayoutRegion} this
42020          */
42021         "slideshow" : true,
42022         /**
42023          * @event slidehide
42024          * Fires when this region slides out of view. 
42025          * @param {Roo.LayoutRegion} this
42026          */
42027         "slidehide" : true,
42028         /**
42029          * @event panelactivated
42030          * Fires when a panel is activated. 
42031          * @param {Roo.LayoutRegion} this
42032          * @param {Roo.ContentPanel} panel The activated panel
42033          */
42034         "panelactivated" : true,
42035         /**
42036          * @event resized
42037          * Fires when the user resizes this region. 
42038          * @param {Roo.LayoutRegion} this
42039          * @param {Number} newSize The new size (width for east/west, height for north/south)
42040          */
42041         "resized" : true
42042     };
42043     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42044     this.panels = new Roo.util.MixedCollection();
42045     this.panels.getKey = this.getPanelId.createDelegate(this);
42046     this.box = null;
42047     this.activePanel = null;
42048     // ensure listeners are added...
42049     
42050     if (config.listeners || config.events) {
42051         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42052             listeners : config.listeners || {},
42053             events : config.events || {}
42054         });
42055     }
42056     
42057     if(skipConfig !== true){
42058         this.applyConfig(config);
42059     }
42060 };
42061
42062 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42063 {
42064     getPanelId : function(p){
42065         return p.getId();
42066     },
42067     
42068     applyConfig : function(config){
42069         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42070         this.config = config;
42071         
42072     },
42073     
42074     /**
42075      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42076      * the width, for horizontal (north, south) the height.
42077      * @param {Number} newSize The new width or height
42078      */
42079     resizeTo : function(newSize){
42080         var el = this.el ? this.el :
42081                  (this.activePanel ? this.activePanel.getEl() : null);
42082         if(el){
42083             switch(this.position){
42084                 case "east":
42085                 case "west":
42086                     el.setWidth(newSize);
42087                     this.fireEvent("resized", this, newSize);
42088                 break;
42089                 case "north":
42090                 case "south":
42091                     el.setHeight(newSize);
42092                     this.fireEvent("resized", this, newSize);
42093                 break;                
42094             }
42095         }
42096     },
42097     
42098     getBox : function(){
42099         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42100     },
42101     
42102     getMargins : function(){
42103         return this.margins;
42104     },
42105     
42106     updateBox : function(box){
42107         this.box = box;
42108         var el = this.activePanel.getEl();
42109         el.dom.style.left = box.x + "px";
42110         el.dom.style.top = box.y + "px";
42111         this.activePanel.setSize(box.width, box.height);
42112     },
42113     
42114     /**
42115      * Returns the container element for this region.
42116      * @return {Roo.Element}
42117      */
42118     getEl : function(){
42119         return this.activePanel;
42120     },
42121     
42122     /**
42123      * Returns true if this region is currently visible.
42124      * @return {Boolean}
42125      */
42126     isVisible : function(){
42127         return this.activePanel ? true : false;
42128     },
42129     
42130     setActivePanel : function(panel){
42131         panel = this.getPanel(panel);
42132         if(this.activePanel && this.activePanel != panel){
42133             this.activePanel.setActiveState(false);
42134             this.activePanel.getEl().setLeftTop(-10000,-10000);
42135         }
42136         this.activePanel = panel;
42137         panel.setActiveState(true);
42138         if(this.box){
42139             panel.setSize(this.box.width, this.box.height);
42140         }
42141         this.fireEvent("panelactivated", this, panel);
42142         this.fireEvent("invalidated");
42143     },
42144     
42145     /**
42146      * Show the specified panel.
42147      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42148      * @return {Roo.ContentPanel} The shown panel or null
42149      */
42150     showPanel : function(panel){
42151         panel = this.getPanel(panel);
42152         if(panel){
42153             this.setActivePanel(panel);
42154         }
42155         return panel;
42156     },
42157     
42158     /**
42159      * Get the active panel for this region.
42160      * @return {Roo.ContentPanel} The active panel or null
42161      */
42162     getActivePanel : function(){
42163         return this.activePanel;
42164     },
42165     
42166     /**
42167      * Add the passed ContentPanel(s)
42168      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42169      * @return {Roo.ContentPanel} The panel added (if only one was added)
42170      */
42171     add : function(panel){
42172         if(arguments.length > 1){
42173             for(var i = 0, len = arguments.length; i < len; i++) {
42174                 this.add(arguments[i]);
42175             }
42176             return null;
42177         }
42178         if(this.hasPanel(panel)){
42179             this.showPanel(panel);
42180             return panel;
42181         }
42182         var el = panel.getEl();
42183         if(el.dom.parentNode != this.mgr.el.dom){
42184             this.mgr.el.dom.appendChild(el.dom);
42185         }
42186         if(panel.setRegion){
42187             panel.setRegion(this);
42188         }
42189         this.panels.add(panel);
42190         el.setStyle("position", "absolute");
42191         if(!panel.background){
42192             this.setActivePanel(panel);
42193             if(this.config.initialSize && this.panels.getCount()==1){
42194                 this.resizeTo(this.config.initialSize);
42195             }
42196         }
42197         this.fireEvent("paneladded", this, panel);
42198         return panel;
42199     },
42200     
42201     /**
42202      * Returns true if the panel is in this region.
42203      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42204      * @return {Boolean}
42205      */
42206     hasPanel : function(panel){
42207         if(typeof panel == "object"){ // must be panel obj
42208             panel = panel.getId();
42209         }
42210         return this.getPanel(panel) ? true : false;
42211     },
42212     
42213     /**
42214      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42215      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42216      * @param {Boolean} preservePanel Overrides the config preservePanel option
42217      * @return {Roo.ContentPanel} The panel that was removed
42218      */
42219     remove : function(panel, preservePanel){
42220         panel = this.getPanel(panel);
42221         if(!panel){
42222             return null;
42223         }
42224         var e = {};
42225         this.fireEvent("beforeremove", this, panel, e);
42226         if(e.cancel === true){
42227             return null;
42228         }
42229         var panelId = panel.getId();
42230         this.panels.removeKey(panelId);
42231         return panel;
42232     },
42233     
42234     /**
42235      * Returns the panel specified or null if it's not in this region.
42236      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42237      * @return {Roo.ContentPanel}
42238      */
42239     getPanel : function(id){
42240         if(typeof id == "object"){ // must be panel obj
42241             return id;
42242         }
42243         return this.panels.get(id);
42244     },
42245     
42246     /**
42247      * Returns this regions position (north/south/east/west/center).
42248      * @return {String} 
42249      */
42250     getPosition: function(){
42251         return this.position;    
42252     }
42253 });/*
42254  * Based on:
42255  * Ext JS Library 1.1.1
42256  * Copyright(c) 2006-2007, Ext JS, LLC.
42257  *
42258  * Originally Released Under LGPL - original licence link has changed is not relivant.
42259  *
42260  * Fork - LGPL
42261  * <script type="text/javascript">
42262  */
42263  
42264 /**
42265  * @class Roo.bootstrap.layout.Region
42266  * @extends Roo.bootstrap.layout.Basic
42267  * This class represents a region in a layout manager.
42268  
42269  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
42270  * @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})
42271  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
42272  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
42273  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
42274  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
42275  * @cfg {String}    title           The title for the region (overrides panel titles)
42276  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
42277  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
42278  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
42279  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
42280  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
42281  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
42282  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
42283  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
42284  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
42285  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
42286
42287  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
42288  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
42289  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
42290  * @cfg {Number}    width           For East/West panels
42291  * @cfg {Number}    height          For North/South panels
42292  * @cfg {Boolean}   split           To show the splitter
42293  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
42294  * 
42295  * @cfg {string}   cls             Extra CSS classes to add to region
42296  * 
42297  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42298  * @cfg {string}   region  the region that it inhabits..
42299  *
42300
42301  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
42302  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
42303
42304  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
42305  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
42306  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
42307  */
42308 Roo.bootstrap.layout.Region = function(config)
42309 {
42310     this.applyConfig(config);
42311
42312     var mgr = config.mgr;
42313     var pos = config.region;
42314     config.skipConfig = true;
42315     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
42316     
42317     if (mgr.el) {
42318         this.onRender(mgr.el);   
42319     }
42320      
42321     this.visible = true;
42322     this.collapsed = false;
42323     this.unrendered_panels = [];
42324 };
42325
42326 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
42327
42328     position: '', // set by wrapper (eg. north/south etc..)
42329     unrendered_panels : null,  // unrendered panels.
42330     
42331     tabPosition : false,
42332     
42333     mgr: false, // points to 'Border'
42334     
42335     
42336     createBody : function(){
42337         /** This region's body element 
42338         * @type Roo.Element */
42339         this.bodyEl = this.el.createChild({
42340                 tag: "div",
42341                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
42342         });
42343     },
42344
42345     onRender: function(ctr, pos)
42346     {
42347         var dh = Roo.DomHelper;
42348         /** This region's container element 
42349         * @type Roo.Element */
42350         this.el = dh.append(ctr.dom, {
42351                 tag: "div",
42352                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
42353             }, true);
42354         /** This region's title element 
42355         * @type Roo.Element */
42356     
42357         this.titleEl = dh.append(this.el.dom,  {
42358                 tag: "div",
42359                 unselectable: "on",
42360                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
42361                 children:[
42362                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
42363                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
42364                 ]
42365             }, true);
42366         
42367         this.titleEl.enableDisplayMode();
42368         /** This region's title text element 
42369         * @type HTMLElement */
42370         this.titleTextEl = this.titleEl.dom.firstChild;
42371         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
42372         /*
42373         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
42374         this.closeBtn.enableDisplayMode();
42375         this.closeBtn.on("click", this.closeClicked, this);
42376         this.closeBtn.hide();
42377     */
42378         this.createBody(this.config);
42379         if(this.config.hideWhenEmpty){
42380             this.hide();
42381             this.on("paneladded", this.validateVisibility, this);
42382             this.on("panelremoved", this.validateVisibility, this);
42383         }
42384         if(this.autoScroll){
42385             this.bodyEl.setStyle("overflow", "auto");
42386         }else{
42387             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
42388         }
42389         //if(c.titlebar !== false){
42390             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
42391                 this.titleEl.hide();
42392             }else{
42393                 this.titleEl.show();
42394                 if(this.config.title){
42395                     this.titleTextEl.innerHTML = this.config.title;
42396                 }
42397             }
42398         //}
42399         if(this.config.collapsed){
42400             this.collapse(true);
42401         }
42402         if(this.config.hidden){
42403             this.hide();
42404         }
42405         
42406         if (this.unrendered_panels && this.unrendered_panels.length) {
42407             for (var i =0;i< this.unrendered_panels.length; i++) {
42408                 this.add(this.unrendered_panels[i]);
42409             }
42410             this.unrendered_panels = null;
42411             
42412         }
42413         
42414     },
42415     
42416     applyConfig : function(c)
42417     {
42418         /*
42419          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
42420             var dh = Roo.DomHelper;
42421             if(c.titlebar !== false){
42422                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
42423                 this.collapseBtn.on("click", this.collapse, this);
42424                 this.collapseBtn.enableDisplayMode();
42425                 /*
42426                 if(c.showPin === true || this.showPin){
42427                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
42428                     this.stickBtn.enableDisplayMode();
42429                     this.stickBtn.on("click", this.expand, this);
42430                     this.stickBtn.hide();
42431                 }
42432                 
42433             }
42434             */
42435             /** This region's collapsed element
42436             * @type Roo.Element */
42437             /*
42438              *
42439             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
42440                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
42441             ]}, true);
42442             
42443             if(c.floatable !== false){
42444                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
42445                this.collapsedEl.on("click", this.collapseClick, this);
42446             }
42447
42448             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
42449                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
42450                    id: "message", unselectable: "on", style:{"float":"left"}});
42451                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
42452              }
42453             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
42454             this.expandBtn.on("click", this.expand, this);
42455             
42456         }
42457         
42458         if(this.collapseBtn){
42459             this.collapseBtn.setVisible(c.collapsible == true);
42460         }
42461         
42462         this.cmargins = c.cmargins || this.cmargins ||
42463                          (this.position == "west" || this.position == "east" ?
42464                              {top: 0, left: 2, right:2, bottom: 0} :
42465                              {top: 2, left: 0, right:0, bottom: 2});
42466         */
42467         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42468         
42469         
42470         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
42471         
42472         this.autoScroll = c.autoScroll || false;
42473         
42474         
42475        
42476         
42477         this.duration = c.duration || .30;
42478         this.slideDuration = c.slideDuration || .45;
42479         this.config = c;
42480        
42481     },
42482     /**
42483      * Returns true if this region is currently visible.
42484      * @return {Boolean}
42485      */
42486     isVisible : function(){
42487         return this.visible;
42488     },
42489
42490     /**
42491      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
42492      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
42493      */
42494     //setCollapsedTitle : function(title){
42495     //    title = title || "&#160;";
42496      //   if(this.collapsedTitleTextEl){
42497       //      this.collapsedTitleTextEl.innerHTML = title;
42498        // }
42499     //},
42500
42501     getBox : function(){
42502         var b;
42503       //  if(!this.collapsed){
42504             b = this.el.getBox(false, true);
42505        // }else{
42506           //  b = this.collapsedEl.getBox(false, true);
42507         //}
42508         return b;
42509     },
42510
42511     getMargins : function(){
42512         return this.margins;
42513         //return this.collapsed ? this.cmargins : this.margins;
42514     },
42515 /*
42516     highlight : function(){
42517         this.el.addClass("x-layout-panel-dragover");
42518     },
42519
42520     unhighlight : function(){
42521         this.el.removeClass("x-layout-panel-dragover");
42522     },
42523 */
42524     updateBox : function(box)
42525     {
42526         if (!this.bodyEl) {
42527             return; // not rendered yet..
42528         }
42529         
42530         this.box = box;
42531         if(!this.collapsed){
42532             this.el.dom.style.left = box.x + "px";
42533             this.el.dom.style.top = box.y + "px";
42534             this.updateBody(box.width, box.height);
42535         }else{
42536             this.collapsedEl.dom.style.left = box.x + "px";
42537             this.collapsedEl.dom.style.top = box.y + "px";
42538             this.collapsedEl.setSize(box.width, box.height);
42539         }
42540         if(this.tabs){
42541             this.tabs.autoSizeTabs();
42542         }
42543     },
42544
42545     updateBody : function(w, h)
42546     {
42547         if(w !== null){
42548             this.el.setWidth(w);
42549             w -= this.el.getBorderWidth("rl");
42550             if(this.config.adjustments){
42551                 w += this.config.adjustments[0];
42552             }
42553         }
42554         if(h !== null && h > 0){
42555             this.el.setHeight(h);
42556             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42557             h -= this.el.getBorderWidth("tb");
42558             if(this.config.adjustments){
42559                 h += this.config.adjustments[1];
42560             }
42561             this.bodyEl.setHeight(h);
42562             if(this.tabs){
42563                 h = this.tabs.syncHeight(h);
42564             }
42565         }
42566         if(this.panelSize){
42567             w = w !== null ? w : this.panelSize.width;
42568             h = h !== null ? h : this.panelSize.height;
42569         }
42570         if(this.activePanel){
42571             var el = this.activePanel.getEl();
42572             w = w !== null ? w : el.getWidth();
42573             h = h !== null ? h : el.getHeight();
42574             this.panelSize = {width: w, height: h};
42575             this.activePanel.setSize(w, h);
42576         }
42577         if(Roo.isIE && this.tabs){
42578             this.tabs.el.repaint();
42579         }
42580     },
42581
42582     /**
42583      * Returns the container element for this region.
42584      * @return {Roo.Element}
42585      */
42586     getEl : function(){
42587         return this.el;
42588     },
42589
42590     /**
42591      * Hides this region.
42592      */
42593     hide : function(){
42594         //if(!this.collapsed){
42595             this.el.dom.style.left = "-2000px";
42596             this.el.hide();
42597         //}else{
42598          //   this.collapsedEl.dom.style.left = "-2000px";
42599          //   this.collapsedEl.hide();
42600        // }
42601         this.visible = false;
42602         this.fireEvent("visibilitychange", this, false);
42603     },
42604
42605     /**
42606      * Shows this region if it was previously hidden.
42607      */
42608     show : function(){
42609         //if(!this.collapsed){
42610             this.el.show();
42611         //}else{
42612         //    this.collapsedEl.show();
42613        // }
42614         this.visible = true;
42615         this.fireEvent("visibilitychange", this, true);
42616     },
42617 /*
42618     closeClicked : function(){
42619         if(this.activePanel){
42620             this.remove(this.activePanel);
42621         }
42622     },
42623
42624     collapseClick : function(e){
42625         if(this.isSlid){
42626            e.stopPropagation();
42627            this.slideIn();
42628         }else{
42629            e.stopPropagation();
42630            this.slideOut();
42631         }
42632     },
42633 */
42634     /**
42635      * Collapses this region.
42636      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42637      */
42638     /*
42639     collapse : function(skipAnim, skipCheck = false){
42640         if(this.collapsed) {
42641             return;
42642         }
42643         
42644         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42645             
42646             this.collapsed = true;
42647             if(this.split){
42648                 this.split.el.hide();
42649             }
42650             if(this.config.animate && skipAnim !== true){
42651                 this.fireEvent("invalidated", this);
42652                 this.animateCollapse();
42653             }else{
42654                 this.el.setLocation(-20000,-20000);
42655                 this.el.hide();
42656                 this.collapsedEl.show();
42657                 this.fireEvent("collapsed", this);
42658                 this.fireEvent("invalidated", this);
42659             }
42660         }
42661         
42662     },
42663 */
42664     animateCollapse : function(){
42665         // overridden
42666     },
42667
42668     /**
42669      * Expands this region if it was previously collapsed.
42670      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42671      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42672      */
42673     /*
42674     expand : function(e, skipAnim){
42675         if(e) {
42676             e.stopPropagation();
42677         }
42678         if(!this.collapsed || this.el.hasActiveFx()) {
42679             return;
42680         }
42681         if(this.isSlid){
42682             this.afterSlideIn();
42683             skipAnim = true;
42684         }
42685         this.collapsed = false;
42686         if(this.config.animate && skipAnim !== true){
42687             this.animateExpand();
42688         }else{
42689             this.el.show();
42690             if(this.split){
42691                 this.split.el.show();
42692             }
42693             this.collapsedEl.setLocation(-2000,-2000);
42694             this.collapsedEl.hide();
42695             this.fireEvent("invalidated", this);
42696             this.fireEvent("expanded", this);
42697         }
42698     },
42699 */
42700     animateExpand : function(){
42701         // overridden
42702     },
42703
42704     initTabs : function()
42705     {
42706         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42707         
42708         var ts = new Roo.bootstrap.panel.Tabs({
42709             el: this.bodyEl.dom,
42710             region : this,
42711             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42712             disableTooltips: this.config.disableTabTips,
42713             toolbar : this.config.toolbar
42714         });
42715         
42716         if(this.config.hideTabs){
42717             ts.stripWrap.setDisplayed(false);
42718         }
42719         this.tabs = ts;
42720         ts.resizeTabs = this.config.resizeTabs === true;
42721         ts.minTabWidth = this.config.minTabWidth || 40;
42722         ts.maxTabWidth = this.config.maxTabWidth || 250;
42723         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42724         ts.monitorResize = false;
42725         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42726         ts.bodyEl.addClass('roo-layout-tabs-body');
42727         this.panels.each(this.initPanelAsTab, this);
42728     },
42729
42730     initPanelAsTab : function(panel){
42731         var ti = this.tabs.addTab(
42732             panel.getEl().id,
42733             panel.getTitle(),
42734             null,
42735             this.config.closeOnTab && panel.isClosable(),
42736             panel.tpl
42737         );
42738         if(panel.tabTip !== undefined){
42739             ti.setTooltip(panel.tabTip);
42740         }
42741         ti.on("activate", function(){
42742               this.setActivePanel(panel);
42743         }, this);
42744         
42745         if(this.config.closeOnTab){
42746             ti.on("beforeclose", function(t, e){
42747                 e.cancel = true;
42748                 this.remove(panel);
42749             }, this);
42750         }
42751         
42752         panel.tabItem = ti;
42753         
42754         return ti;
42755     },
42756
42757     updatePanelTitle : function(panel, title)
42758     {
42759         if(this.activePanel == panel){
42760             this.updateTitle(title);
42761         }
42762         if(this.tabs){
42763             var ti = this.tabs.getTab(panel.getEl().id);
42764             ti.setText(title);
42765             if(panel.tabTip !== undefined){
42766                 ti.setTooltip(panel.tabTip);
42767             }
42768         }
42769     },
42770
42771     updateTitle : function(title){
42772         if(this.titleTextEl && !this.config.title){
42773             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42774         }
42775     },
42776
42777     setActivePanel : function(panel)
42778     {
42779         panel = this.getPanel(panel);
42780         if(this.activePanel && this.activePanel != panel){
42781             if(this.activePanel.setActiveState(false) === false){
42782                 return;
42783             }
42784         }
42785         this.activePanel = panel;
42786         panel.setActiveState(true);
42787         if(this.panelSize){
42788             panel.setSize(this.panelSize.width, this.panelSize.height);
42789         }
42790         if(this.closeBtn){
42791             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42792         }
42793         this.updateTitle(panel.getTitle());
42794         if(this.tabs){
42795             this.fireEvent("invalidated", this);
42796         }
42797         this.fireEvent("panelactivated", this, panel);
42798     },
42799
42800     /**
42801      * Shows the specified panel.
42802      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42803      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42804      */
42805     showPanel : function(panel)
42806     {
42807         panel = this.getPanel(panel);
42808         if(panel){
42809             if(this.tabs){
42810                 var tab = this.tabs.getTab(panel.getEl().id);
42811                 if(tab.isHidden()){
42812                     this.tabs.unhideTab(tab.id);
42813                 }
42814                 tab.activate();
42815             }else{
42816                 this.setActivePanel(panel);
42817             }
42818         }
42819         return panel;
42820     },
42821
42822     /**
42823      * Get the active panel for this region.
42824      * @return {Roo.ContentPanel} The active panel or null
42825      */
42826     getActivePanel : function(){
42827         return this.activePanel;
42828     },
42829
42830     validateVisibility : function(){
42831         if(this.panels.getCount() < 1){
42832             this.updateTitle("&#160;");
42833             this.closeBtn.hide();
42834             this.hide();
42835         }else{
42836             if(!this.isVisible()){
42837                 this.show();
42838             }
42839         }
42840     },
42841
42842     /**
42843      * Adds the passed ContentPanel(s) to this region.
42844      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42845      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42846      */
42847     add : function(panel)
42848     {
42849         if(arguments.length > 1){
42850             for(var i = 0, len = arguments.length; i < len; i++) {
42851                 this.add(arguments[i]);
42852             }
42853             return null;
42854         }
42855         
42856         // if we have not been rendered yet, then we can not really do much of this..
42857         if (!this.bodyEl) {
42858             this.unrendered_panels.push(panel);
42859             return panel;
42860         }
42861         
42862         
42863         
42864         
42865         if(this.hasPanel(panel)){
42866             this.showPanel(panel);
42867             return panel;
42868         }
42869         panel.setRegion(this);
42870         this.panels.add(panel);
42871        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42872             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42873             // and hide them... ???
42874             this.bodyEl.dom.appendChild(panel.getEl().dom);
42875             if(panel.background !== true){
42876                 this.setActivePanel(panel);
42877             }
42878             this.fireEvent("paneladded", this, panel);
42879             return panel;
42880         }
42881         */
42882         if(!this.tabs){
42883             this.initTabs();
42884         }else{
42885             this.initPanelAsTab(panel);
42886         }
42887         
42888         
42889         if(panel.background !== true){
42890             this.tabs.activate(panel.getEl().id);
42891         }
42892         this.fireEvent("paneladded", this, panel);
42893         return panel;
42894     },
42895
42896     /**
42897      * Hides the tab for the specified panel.
42898      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42899      */
42900     hidePanel : function(panel){
42901         if(this.tabs && (panel = this.getPanel(panel))){
42902             this.tabs.hideTab(panel.getEl().id);
42903         }
42904     },
42905
42906     /**
42907      * Unhides the tab for a previously hidden panel.
42908      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42909      */
42910     unhidePanel : function(panel){
42911         if(this.tabs && (panel = this.getPanel(panel))){
42912             this.tabs.unhideTab(panel.getEl().id);
42913         }
42914     },
42915
42916     clearPanels : function(){
42917         while(this.panels.getCount() > 0){
42918              this.remove(this.panels.first());
42919         }
42920     },
42921
42922     /**
42923      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42924      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42925      * @param {Boolean} preservePanel Overrides the config preservePanel option
42926      * @return {Roo.ContentPanel} The panel that was removed
42927      */
42928     remove : function(panel, preservePanel)
42929     {
42930         panel = this.getPanel(panel);
42931         if(!panel){
42932             return null;
42933         }
42934         var e = {};
42935         this.fireEvent("beforeremove", this, panel, e);
42936         if(e.cancel === true){
42937             return null;
42938         }
42939         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42940         var panelId = panel.getId();
42941         this.panels.removeKey(panelId);
42942         if(preservePanel){
42943             document.body.appendChild(panel.getEl().dom);
42944         }
42945         if(this.tabs){
42946             this.tabs.removeTab(panel.getEl().id);
42947         }else if (!preservePanel){
42948             this.bodyEl.dom.removeChild(panel.getEl().dom);
42949         }
42950         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42951             var p = this.panels.first();
42952             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42953             tempEl.appendChild(p.getEl().dom);
42954             this.bodyEl.update("");
42955             this.bodyEl.dom.appendChild(p.getEl().dom);
42956             tempEl = null;
42957             this.updateTitle(p.getTitle());
42958             this.tabs = null;
42959             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42960             this.setActivePanel(p);
42961         }
42962         panel.setRegion(null);
42963         if(this.activePanel == panel){
42964             this.activePanel = null;
42965         }
42966         if(this.config.autoDestroy !== false && preservePanel !== true){
42967             try{panel.destroy();}catch(e){}
42968         }
42969         this.fireEvent("panelremoved", this, panel);
42970         return panel;
42971     },
42972
42973     /**
42974      * Returns the TabPanel component used by this region
42975      * @return {Roo.TabPanel}
42976      */
42977     getTabs : function(){
42978         return this.tabs;
42979     },
42980
42981     createTool : function(parentEl, className){
42982         var btn = Roo.DomHelper.append(parentEl, {
42983             tag: "div",
42984             cls: "x-layout-tools-button",
42985             children: [ {
42986                 tag: "div",
42987                 cls: "roo-layout-tools-button-inner " + className,
42988                 html: "&#160;"
42989             }]
42990         }, true);
42991         btn.addClassOnOver("roo-layout-tools-button-over");
42992         return btn;
42993     }
42994 });/*
42995  * Based on:
42996  * Ext JS Library 1.1.1
42997  * Copyright(c) 2006-2007, Ext JS, LLC.
42998  *
42999  * Originally Released Under LGPL - original licence link has changed is not relivant.
43000  *
43001  * Fork - LGPL
43002  * <script type="text/javascript">
43003  */
43004  
43005
43006
43007 /**
43008  * @class Roo.SplitLayoutRegion
43009  * @extends Roo.LayoutRegion
43010  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43011  */
43012 Roo.bootstrap.layout.Split = function(config){
43013     this.cursor = config.cursor;
43014     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43015 };
43016
43017 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43018 {
43019     splitTip : "Drag to resize.",
43020     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43021     useSplitTips : false,
43022
43023     applyConfig : function(config){
43024         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43025     },
43026     
43027     onRender : function(ctr,pos) {
43028         
43029         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43030         if(!this.config.split){
43031             return;
43032         }
43033         if(!this.split){
43034             
43035             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43036                             tag: "div",
43037                             id: this.el.id + "-split",
43038                             cls: "roo-layout-split roo-layout-split-"+this.position,
43039                             html: "&#160;"
43040             });
43041             /** The SplitBar for this region 
43042             * @type Roo.SplitBar */
43043             // does not exist yet...
43044             Roo.log([this.position, this.orientation]);
43045             
43046             this.split = new Roo.bootstrap.SplitBar({
43047                 dragElement : splitEl,
43048                 resizingElement: this.el,
43049                 orientation : this.orientation
43050             });
43051             
43052             this.split.on("moved", this.onSplitMove, this);
43053             this.split.useShim = this.config.useShim === true;
43054             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43055             if(this.useSplitTips){
43056                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43057             }
43058             //if(config.collapsible){
43059             //    this.split.el.on("dblclick", this.collapse,  this);
43060             //}
43061         }
43062         if(typeof this.config.minSize != "undefined"){
43063             this.split.minSize = this.config.minSize;
43064         }
43065         if(typeof this.config.maxSize != "undefined"){
43066             this.split.maxSize = this.config.maxSize;
43067         }
43068         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43069             this.hideSplitter();
43070         }
43071         
43072     },
43073
43074     getHMaxSize : function(){
43075          var cmax = this.config.maxSize || 10000;
43076          var center = this.mgr.getRegion("center");
43077          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43078     },
43079
43080     getVMaxSize : function(){
43081          var cmax = this.config.maxSize || 10000;
43082          var center = this.mgr.getRegion("center");
43083          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43084     },
43085
43086     onSplitMove : function(split, newSize){
43087         this.fireEvent("resized", this, newSize);
43088     },
43089     
43090     /** 
43091      * Returns the {@link Roo.SplitBar} for this region.
43092      * @return {Roo.SplitBar}
43093      */
43094     getSplitBar : function(){
43095         return this.split;
43096     },
43097     
43098     hide : function(){
43099         this.hideSplitter();
43100         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43101     },
43102
43103     hideSplitter : function(){
43104         if(this.split){
43105             this.split.el.setLocation(-2000,-2000);
43106             this.split.el.hide();
43107         }
43108     },
43109
43110     show : function(){
43111         if(this.split){
43112             this.split.el.show();
43113         }
43114         Roo.bootstrap.layout.Split.superclass.show.call(this);
43115     },
43116     
43117     beforeSlide: function(){
43118         if(Roo.isGecko){// firefox overflow auto bug workaround
43119             this.bodyEl.clip();
43120             if(this.tabs) {
43121                 this.tabs.bodyEl.clip();
43122             }
43123             if(this.activePanel){
43124                 this.activePanel.getEl().clip();
43125                 
43126                 if(this.activePanel.beforeSlide){
43127                     this.activePanel.beforeSlide();
43128                 }
43129             }
43130         }
43131     },
43132     
43133     afterSlide : function(){
43134         if(Roo.isGecko){// firefox overflow auto bug workaround
43135             this.bodyEl.unclip();
43136             if(this.tabs) {
43137                 this.tabs.bodyEl.unclip();
43138             }
43139             if(this.activePanel){
43140                 this.activePanel.getEl().unclip();
43141                 if(this.activePanel.afterSlide){
43142                     this.activePanel.afterSlide();
43143                 }
43144             }
43145         }
43146     },
43147
43148     initAutoHide : function(){
43149         if(this.autoHide !== false){
43150             if(!this.autoHideHd){
43151                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43152                 this.autoHideHd = {
43153                     "mouseout": function(e){
43154                         if(!e.within(this.el, true)){
43155                             st.delay(500);
43156                         }
43157                     },
43158                     "mouseover" : function(e){
43159                         st.cancel();
43160                     },
43161                     scope : this
43162                 };
43163             }
43164             this.el.on(this.autoHideHd);
43165         }
43166     },
43167
43168     clearAutoHide : function(){
43169         if(this.autoHide !== false){
43170             this.el.un("mouseout", this.autoHideHd.mouseout);
43171             this.el.un("mouseover", this.autoHideHd.mouseover);
43172         }
43173     },
43174
43175     clearMonitor : function(){
43176         Roo.get(document).un("click", this.slideInIf, this);
43177     },
43178
43179     // these names are backwards but not changed for compat
43180     slideOut : function(){
43181         if(this.isSlid || this.el.hasActiveFx()){
43182             return;
43183         }
43184         this.isSlid = true;
43185         if(this.collapseBtn){
43186             this.collapseBtn.hide();
43187         }
43188         this.closeBtnState = this.closeBtn.getStyle('display');
43189         this.closeBtn.hide();
43190         if(this.stickBtn){
43191             this.stickBtn.show();
43192         }
43193         this.el.show();
43194         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43195         this.beforeSlide();
43196         this.el.setStyle("z-index", 10001);
43197         this.el.slideIn(this.getSlideAnchor(), {
43198             callback: function(){
43199                 this.afterSlide();
43200                 this.initAutoHide();
43201                 Roo.get(document).on("click", this.slideInIf, this);
43202                 this.fireEvent("slideshow", this);
43203             },
43204             scope: this,
43205             block: true
43206         });
43207     },
43208
43209     afterSlideIn : function(){
43210         this.clearAutoHide();
43211         this.isSlid = false;
43212         this.clearMonitor();
43213         this.el.setStyle("z-index", "");
43214         if(this.collapseBtn){
43215             this.collapseBtn.show();
43216         }
43217         this.closeBtn.setStyle('display', this.closeBtnState);
43218         if(this.stickBtn){
43219             this.stickBtn.hide();
43220         }
43221         this.fireEvent("slidehide", this);
43222     },
43223
43224     slideIn : function(cb){
43225         if(!this.isSlid || this.el.hasActiveFx()){
43226             Roo.callback(cb);
43227             return;
43228         }
43229         this.isSlid = false;
43230         this.beforeSlide();
43231         this.el.slideOut(this.getSlideAnchor(), {
43232             callback: function(){
43233                 this.el.setLeftTop(-10000, -10000);
43234                 this.afterSlide();
43235                 this.afterSlideIn();
43236                 Roo.callback(cb);
43237             },
43238             scope: this,
43239             block: true
43240         });
43241     },
43242     
43243     slideInIf : function(e){
43244         if(!e.within(this.el)){
43245             this.slideIn();
43246         }
43247     },
43248
43249     animateCollapse : function(){
43250         this.beforeSlide();
43251         this.el.setStyle("z-index", 20000);
43252         var anchor = this.getSlideAnchor();
43253         this.el.slideOut(anchor, {
43254             callback : function(){
43255                 this.el.setStyle("z-index", "");
43256                 this.collapsedEl.slideIn(anchor, {duration:.3});
43257                 this.afterSlide();
43258                 this.el.setLocation(-10000,-10000);
43259                 this.el.hide();
43260                 this.fireEvent("collapsed", this);
43261             },
43262             scope: this,
43263             block: true
43264         });
43265     },
43266
43267     animateExpand : function(){
43268         this.beforeSlide();
43269         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
43270         this.el.setStyle("z-index", 20000);
43271         this.collapsedEl.hide({
43272             duration:.1
43273         });
43274         this.el.slideIn(this.getSlideAnchor(), {
43275             callback : function(){
43276                 this.el.setStyle("z-index", "");
43277                 this.afterSlide();
43278                 if(this.split){
43279                     this.split.el.show();
43280                 }
43281                 this.fireEvent("invalidated", this);
43282                 this.fireEvent("expanded", this);
43283             },
43284             scope: this,
43285             block: true
43286         });
43287     },
43288
43289     anchors : {
43290         "west" : "left",
43291         "east" : "right",
43292         "north" : "top",
43293         "south" : "bottom"
43294     },
43295
43296     sanchors : {
43297         "west" : "l",
43298         "east" : "r",
43299         "north" : "t",
43300         "south" : "b"
43301     },
43302
43303     canchors : {
43304         "west" : "tl-tr",
43305         "east" : "tr-tl",
43306         "north" : "tl-bl",
43307         "south" : "bl-tl"
43308     },
43309
43310     getAnchor : function(){
43311         return this.anchors[this.position];
43312     },
43313
43314     getCollapseAnchor : function(){
43315         return this.canchors[this.position];
43316     },
43317
43318     getSlideAnchor : function(){
43319         return this.sanchors[this.position];
43320     },
43321
43322     getAlignAdj : function(){
43323         var cm = this.cmargins;
43324         switch(this.position){
43325             case "west":
43326                 return [0, 0];
43327             break;
43328             case "east":
43329                 return [0, 0];
43330             break;
43331             case "north":
43332                 return [0, 0];
43333             break;
43334             case "south":
43335                 return [0, 0];
43336             break;
43337         }
43338     },
43339
43340     getExpandAdj : function(){
43341         var c = this.collapsedEl, cm = this.cmargins;
43342         switch(this.position){
43343             case "west":
43344                 return [-(cm.right+c.getWidth()+cm.left), 0];
43345             break;
43346             case "east":
43347                 return [cm.right+c.getWidth()+cm.left, 0];
43348             break;
43349             case "north":
43350                 return [0, -(cm.top+cm.bottom+c.getHeight())];
43351             break;
43352             case "south":
43353                 return [0, cm.top+cm.bottom+c.getHeight()];
43354             break;
43355         }
43356     }
43357 });/*
43358  * Based on:
43359  * Ext JS Library 1.1.1
43360  * Copyright(c) 2006-2007, Ext JS, LLC.
43361  *
43362  * Originally Released Under LGPL - original licence link has changed is not relivant.
43363  *
43364  * Fork - LGPL
43365  * <script type="text/javascript">
43366  */
43367 /*
43368  * These classes are private internal classes
43369  */
43370 Roo.bootstrap.layout.Center = function(config){
43371     config.region = "center";
43372     Roo.bootstrap.layout.Region.call(this, config);
43373     this.visible = true;
43374     this.minWidth = config.minWidth || 20;
43375     this.minHeight = config.minHeight || 20;
43376 };
43377
43378 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
43379     hide : function(){
43380         // center panel can't be hidden
43381     },
43382     
43383     show : function(){
43384         // center panel can't be hidden
43385     },
43386     
43387     getMinWidth: function(){
43388         return this.minWidth;
43389     },
43390     
43391     getMinHeight: function(){
43392         return this.minHeight;
43393     }
43394 });
43395
43396
43397
43398
43399  
43400
43401
43402
43403
43404
43405
43406 Roo.bootstrap.layout.North = function(config)
43407 {
43408     config.region = 'north';
43409     config.cursor = 'n-resize';
43410     
43411     Roo.bootstrap.layout.Split.call(this, config);
43412     
43413     
43414     if(this.split){
43415         this.split.placement = Roo.bootstrap.SplitBar.TOP;
43416         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
43417         this.split.el.addClass("roo-layout-split-v");
43418     }
43419     //var size = config.initialSize || config.height;
43420     //if(this.el && typeof size != "undefined"){
43421     //    this.el.setHeight(size);
43422     //}
43423 };
43424 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
43425 {
43426     orientation: Roo.bootstrap.SplitBar.VERTICAL,
43427      
43428      
43429     onRender : function(ctr, pos)
43430     {
43431         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43432         var size = this.config.initialSize || this.config.height;
43433         if(this.el && typeof size != "undefined"){
43434             this.el.setHeight(size);
43435         }
43436     
43437     },
43438     
43439     getBox : function(){
43440         if(this.collapsed){
43441             return this.collapsedEl.getBox();
43442         }
43443         var box = this.el.getBox();
43444         if(this.split){
43445             box.height += this.split.el.getHeight();
43446         }
43447         return box;
43448     },
43449     
43450     updateBox : function(box){
43451         if(this.split && !this.collapsed){
43452             box.height -= this.split.el.getHeight();
43453             this.split.el.setLeft(box.x);
43454             this.split.el.setTop(box.y+box.height);
43455             this.split.el.setWidth(box.width);
43456         }
43457         if(this.collapsed){
43458             this.updateBody(box.width, null);
43459         }
43460         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43461     }
43462 });
43463
43464
43465
43466
43467
43468 Roo.bootstrap.layout.South = function(config){
43469     config.region = 'south';
43470     config.cursor = 's-resize';
43471     Roo.bootstrap.layout.Split.call(this, config);
43472     if(this.split){
43473         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
43474         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
43475         this.split.el.addClass("roo-layout-split-v");
43476     }
43477     
43478 };
43479
43480 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
43481     orientation: Roo.bootstrap.SplitBar.VERTICAL,
43482     
43483     onRender : function(ctr, pos)
43484     {
43485         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43486         var size = this.config.initialSize || this.config.height;
43487         if(this.el && typeof size != "undefined"){
43488             this.el.setHeight(size);
43489         }
43490     
43491     },
43492     
43493     getBox : function(){
43494         if(this.collapsed){
43495             return this.collapsedEl.getBox();
43496         }
43497         var box = this.el.getBox();
43498         if(this.split){
43499             var sh = this.split.el.getHeight();
43500             box.height += sh;
43501             box.y -= sh;
43502         }
43503         return box;
43504     },
43505     
43506     updateBox : function(box){
43507         if(this.split && !this.collapsed){
43508             var sh = this.split.el.getHeight();
43509             box.height -= sh;
43510             box.y += sh;
43511             this.split.el.setLeft(box.x);
43512             this.split.el.setTop(box.y-sh);
43513             this.split.el.setWidth(box.width);
43514         }
43515         if(this.collapsed){
43516             this.updateBody(box.width, null);
43517         }
43518         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43519     }
43520 });
43521
43522 Roo.bootstrap.layout.East = function(config){
43523     config.region = "east";
43524     config.cursor = "e-resize";
43525     Roo.bootstrap.layout.Split.call(this, config);
43526     if(this.split){
43527         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
43528         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43529         this.split.el.addClass("roo-layout-split-h");
43530     }
43531     
43532 };
43533 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43534     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43535     
43536     onRender : function(ctr, pos)
43537     {
43538         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43539         var size = this.config.initialSize || this.config.width;
43540         if(this.el && typeof size != "undefined"){
43541             this.el.setWidth(size);
43542         }
43543     
43544     },
43545     
43546     getBox : function(){
43547         if(this.collapsed){
43548             return this.collapsedEl.getBox();
43549         }
43550         var box = this.el.getBox();
43551         if(this.split){
43552             var sw = this.split.el.getWidth();
43553             box.width += sw;
43554             box.x -= sw;
43555         }
43556         return box;
43557     },
43558
43559     updateBox : function(box){
43560         if(this.split && !this.collapsed){
43561             var sw = this.split.el.getWidth();
43562             box.width -= sw;
43563             this.split.el.setLeft(box.x);
43564             this.split.el.setTop(box.y);
43565             this.split.el.setHeight(box.height);
43566             box.x += sw;
43567         }
43568         if(this.collapsed){
43569             this.updateBody(null, box.height);
43570         }
43571         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43572     }
43573 });
43574
43575 Roo.bootstrap.layout.West = function(config){
43576     config.region = "west";
43577     config.cursor = "w-resize";
43578     
43579     Roo.bootstrap.layout.Split.call(this, config);
43580     if(this.split){
43581         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43582         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43583         this.split.el.addClass("roo-layout-split-h");
43584     }
43585     
43586 };
43587 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43588     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43589     
43590     onRender: function(ctr, pos)
43591     {
43592         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43593         var size = this.config.initialSize || this.config.width;
43594         if(typeof size != "undefined"){
43595             this.el.setWidth(size);
43596         }
43597     },
43598     
43599     getBox : function(){
43600         if(this.collapsed){
43601             return this.collapsedEl.getBox();
43602         }
43603         var box = this.el.getBox();
43604         if (box.width == 0) {
43605             box.width = this.config.width; // kludge?
43606         }
43607         if(this.split){
43608             box.width += this.split.el.getWidth();
43609         }
43610         return box;
43611     },
43612     
43613     updateBox : function(box){
43614         if(this.split && !this.collapsed){
43615             var sw = this.split.el.getWidth();
43616             box.width -= sw;
43617             this.split.el.setLeft(box.x+box.width);
43618             this.split.el.setTop(box.y);
43619             this.split.el.setHeight(box.height);
43620         }
43621         if(this.collapsed){
43622             this.updateBody(null, box.height);
43623         }
43624         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43625     }
43626 });/*
43627  * Based on:
43628  * Ext JS Library 1.1.1
43629  * Copyright(c) 2006-2007, Ext JS, LLC.
43630  *
43631  * Originally Released Under LGPL - original licence link has changed is not relivant.
43632  *
43633  * Fork - LGPL
43634  * <script type="text/javascript">
43635  */
43636 /**
43637  * @class Roo.bootstrap.paenl.Content
43638  * @extends Roo.util.Observable
43639  * @children Roo.bootstrap.Component
43640  * @parent builder Roo.bootstrap.layout.Border
43641  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43642  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43643  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43644  * @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
43645  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43646  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43647  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43648  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43649  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43650  * @cfg {String} title          The title for this panel
43651  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43652  * @cfg {String} url            Calls {@link #setUrl} with this value
43653  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43654  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43655  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43656  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43657  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43658  * @cfg {Boolean} badges render the badges
43659  * @cfg {String} cls  extra classes to use  
43660  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43661  
43662  * @constructor
43663  * Create a new ContentPanel.
43664  * @param {String/Object} config A string to set only the title or a config object
43665  
43666  */
43667 Roo.bootstrap.panel.Content = function( config){
43668     
43669     this.tpl = config.tpl || false;
43670     
43671     var el = config.el;
43672     var content = config.content;
43673
43674     if(config.autoCreate){ // xtype is available if this is called from factory
43675         el = Roo.id();
43676     }
43677     this.el = Roo.get(el);
43678     if(!this.el && config && config.autoCreate){
43679         if(typeof config.autoCreate == "object"){
43680             if(!config.autoCreate.id){
43681                 config.autoCreate.id = config.id||el;
43682             }
43683             this.el = Roo.DomHelper.append(document.body,
43684                         config.autoCreate, true);
43685         }else{
43686             var elcfg =  {
43687                 tag: "div",
43688                 cls: (config.cls || '') +
43689                     (config.background ? ' bg-' + config.background : '') +
43690                     " roo-layout-inactive-content",
43691                 id: config.id||el
43692             };
43693             if (config.iframe) {
43694                 elcfg.cn = [
43695                     {
43696                         tag : 'iframe',
43697                         style : 'border: 0px',
43698                         src : 'about:blank'
43699                     }
43700                 ];
43701             }
43702               
43703             if (config.html) {
43704                 elcfg.html = config.html;
43705                 
43706             }
43707                         
43708             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43709             if (config.iframe) {
43710                 this.iframeEl = this.el.select('iframe',true).first();
43711             }
43712             
43713         }
43714     } 
43715     this.closable = false;
43716     this.loaded = false;
43717     this.active = false;
43718    
43719       
43720     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43721         
43722         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43723         
43724         this.wrapEl = this.el; //this.el.wrap();
43725         var ti = [];
43726         if (config.toolbar.items) {
43727             ti = config.toolbar.items ;
43728             delete config.toolbar.items ;
43729         }
43730         
43731         var nitems = [];
43732         this.toolbar.render(this.wrapEl, 'before');
43733         for(var i =0;i < ti.length;i++) {
43734           //  Roo.log(['add child', items[i]]);
43735             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43736         }
43737         this.toolbar.items = nitems;
43738         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43739         delete config.toolbar;
43740         
43741     }
43742     /*
43743     // xtype created footer. - not sure if will work as we normally have to render first..
43744     if (this.footer && !this.footer.el && this.footer.xtype) {
43745         if (!this.wrapEl) {
43746             this.wrapEl = this.el.wrap();
43747         }
43748     
43749         this.footer.container = this.wrapEl.createChild();
43750          
43751         this.footer = Roo.factory(this.footer, Roo);
43752         
43753     }
43754     */
43755     
43756      if(typeof config == "string"){
43757         this.title = config;
43758     }else{
43759         Roo.apply(this, config);
43760     }
43761     
43762     if(this.resizeEl){
43763         this.resizeEl = Roo.get(this.resizeEl, true);
43764     }else{
43765         this.resizeEl = this.el;
43766     }
43767     // handle view.xtype
43768     
43769  
43770     
43771     
43772     this.addEvents({
43773         /**
43774          * @event activate
43775          * Fires when this panel is activated. 
43776          * @param {Roo.ContentPanel} this
43777          */
43778         "activate" : true,
43779         /**
43780          * @event deactivate
43781          * Fires when this panel is activated. 
43782          * @param {Roo.ContentPanel} this
43783          */
43784         "deactivate" : true,
43785
43786         /**
43787          * @event resize
43788          * Fires when this panel is resized if fitToFrame is true.
43789          * @param {Roo.ContentPanel} this
43790          * @param {Number} width The width after any component adjustments
43791          * @param {Number} height The height after any component adjustments
43792          */
43793         "resize" : true,
43794         
43795          /**
43796          * @event render
43797          * Fires when this tab is created
43798          * @param {Roo.ContentPanel} this
43799          */
43800         "render" : true,
43801         
43802           /**
43803          * @event scroll
43804          * Fires when this content is scrolled
43805          * @param {Roo.ContentPanel} this
43806          * @param {Event} scrollEvent
43807          */
43808         "scroll" : true
43809         
43810         
43811         
43812     });
43813     
43814
43815     
43816     
43817     if(this.autoScroll && !this.iframe){
43818         this.resizeEl.setStyle("overflow", "auto");
43819         this.resizeEl.on('scroll', this.onScroll, this);
43820     } else {
43821         // fix randome scrolling
43822         //this.el.on('scroll', function() {
43823         //    Roo.log('fix random scolling');
43824         //    this.scrollTo('top',0); 
43825         //});
43826     }
43827     content = content || this.content;
43828     if(content){
43829         this.setContent(content);
43830     }
43831     if(config && config.url){
43832         this.setUrl(this.url, this.params, this.loadOnce);
43833     }
43834     
43835     
43836     
43837     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43838     
43839     if (this.view && typeof(this.view.xtype) != 'undefined') {
43840         this.view.el = this.el.appendChild(document.createElement("div"));
43841         this.view = Roo.factory(this.view); 
43842         this.view.render  &&  this.view.render(false, '');  
43843     }
43844     
43845     
43846     this.fireEvent('render', this);
43847 };
43848
43849 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43850     
43851     cls : '',
43852     background : '',
43853     
43854     tabTip : '',
43855     
43856     iframe : false,
43857     iframeEl : false,
43858     
43859     /* Resize Element - use this to work out scroll etc. */
43860     resizeEl : false,
43861     
43862     setRegion : function(region){
43863         this.region = region;
43864         this.setActiveClass(region && !this.background);
43865     },
43866     
43867     
43868     setActiveClass: function(state)
43869     {
43870         if(state){
43871            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43872            this.el.setStyle('position','relative');
43873         }else{
43874            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43875            this.el.setStyle('position', 'absolute');
43876         } 
43877     },
43878     
43879     /**
43880      * Returns the toolbar for this Panel if one was configured. 
43881      * @return {Roo.Toolbar} 
43882      */
43883     getToolbar : function(){
43884         return this.toolbar;
43885     },
43886     
43887     setActiveState : function(active)
43888     {
43889         this.active = active;
43890         this.setActiveClass(active);
43891         if(!active){
43892             if(this.fireEvent("deactivate", this) === false){
43893                 return false;
43894             }
43895             return true;
43896         }
43897         this.fireEvent("activate", this);
43898         return true;
43899     },
43900     /**
43901      * Updates this panel's element (not for iframe)
43902      * @param {String} content The new content
43903      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43904     */
43905     setContent : function(content, loadScripts){
43906         if (this.iframe) {
43907             return;
43908         }
43909         
43910         this.el.update(content, loadScripts);
43911     },
43912
43913     ignoreResize : function(w, h)
43914     {
43915         //return false; // always resize?
43916         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43917             return true;
43918         }else{
43919             this.lastSize = {width: w, height: h};
43920             return false;
43921         }
43922     },
43923     /**
43924      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43925      * @return {Roo.UpdateManager} The UpdateManager
43926      */
43927     getUpdateManager : function(){
43928         if (this.iframe) {
43929             return false;
43930         }
43931         return this.el.getUpdateManager();
43932     },
43933      /**
43934      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43935      * Does not work with IFRAME contents
43936      * @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:
43937 <pre><code>
43938 panel.load({
43939     url: "your-url.php",
43940     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43941     callback: yourFunction,
43942     scope: yourObject, //(optional scope)
43943     discardUrl: false,
43944     nocache: false,
43945     text: "Loading...",
43946     timeout: 30,
43947     scripts: false
43948 });
43949 </code></pre>
43950      
43951      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43952      * 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.
43953      * @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}
43954      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43955      * @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.
43956      * @return {Roo.ContentPanel} this
43957      */
43958     load : function(){
43959         
43960         if (this.iframe) {
43961             return this;
43962         }
43963         
43964         var um = this.el.getUpdateManager();
43965         um.update.apply(um, arguments);
43966         return this;
43967     },
43968
43969
43970     /**
43971      * 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.
43972      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43973      * @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)
43974      * @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)
43975      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43976      */
43977     setUrl : function(url, params, loadOnce){
43978         if (this.iframe) {
43979             this.iframeEl.dom.src = url;
43980             return false;
43981         }
43982         
43983         if(this.refreshDelegate){
43984             this.removeListener("activate", this.refreshDelegate);
43985         }
43986         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43987         this.on("activate", this.refreshDelegate);
43988         return this.el.getUpdateManager();
43989     },
43990     
43991     _handleRefresh : function(url, params, loadOnce){
43992         if(!loadOnce || !this.loaded){
43993             var updater = this.el.getUpdateManager();
43994             updater.update(url, params, this._setLoaded.createDelegate(this));
43995         }
43996     },
43997     
43998     _setLoaded : function(){
43999         this.loaded = true;
44000     }, 
44001     
44002     /**
44003      * Returns this panel's id
44004      * @return {String} 
44005      */
44006     getId : function(){
44007         return this.el.id;
44008     },
44009     
44010     /** 
44011      * Returns this panel's element - used by regiosn to add.
44012      * @return {Roo.Element} 
44013      */
44014     getEl : function(){
44015         return this.wrapEl || this.el;
44016     },
44017     
44018    
44019     
44020     adjustForComponents : function(width, height)
44021     {
44022         //Roo.log('adjustForComponents ');
44023         if(this.resizeEl != this.el){
44024             width -= this.el.getFrameWidth('lr');
44025             height -= this.el.getFrameWidth('tb');
44026         }
44027         if(this.toolbar){
44028             var te = this.toolbar.getEl();
44029             te.setWidth(width);
44030             height -= te.getHeight();
44031         }
44032         if(this.footer){
44033             var te = this.footer.getEl();
44034             te.setWidth(width);
44035             height -= te.getHeight();
44036         }
44037         
44038         
44039         if(this.adjustments){
44040             width += this.adjustments[0];
44041             height += this.adjustments[1];
44042         }
44043         return {"width": width, "height": height};
44044     },
44045     
44046     setSize : function(width, height){
44047         if(this.fitToFrame && !this.ignoreResize(width, height)){
44048             if(this.fitContainer && this.resizeEl != this.el){
44049                 this.el.setSize(width, height);
44050             }
44051             var size = this.adjustForComponents(width, height);
44052             if (this.iframe) {
44053                 this.iframeEl.setSize(width,height);
44054             }
44055             
44056             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44057             this.fireEvent('resize', this, size.width, size.height);
44058             
44059             
44060         }
44061     },
44062     
44063     /**
44064      * Returns this panel's title
44065      * @return {String} 
44066      */
44067     getTitle : function(){
44068         
44069         if (typeof(this.title) != 'object') {
44070             return this.title;
44071         }
44072         
44073         var t = '';
44074         for (var k in this.title) {
44075             if (!this.title.hasOwnProperty(k)) {
44076                 continue;
44077             }
44078             
44079             if (k.indexOf('-') >= 0) {
44080                 var s = k.split('-');
44081                 for (var i = 0; i<s.length; i++) {
44082                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44083                 }
44084             } else {
44085                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44086             }
44087         }
44088         return t;
44089     },
44090     
44091     /**
44092      * Set this panel's title
44093      * @param {String} title
44094      */
44095     setTitle : function(title){
44096         this.title = title;
44097         if(this.region){
44098             this.region.updatePanelTitle(this, title);
44099         }
44100     },
44101     
44102     /**
44103      * Returns true is this panel was configured to be closable
44104      * @return {Boolean} 
44105      */
44106     isClosable : function(){
44107         return this.closable;
44108     },
44109     
44110     beforeSlide : function(){
44111         this.el.clip();
44112         this.resizeEl.clip();
44113     },
44114     
44115     afterSlide : function(){
44116         this.el.unclip();
44117         this.resizeEl.unclip();
44118     },
44119     
44120     /**
44121      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44122      *   Will fail silently if the {@link #setUrl} method has not been called.
44123      *   This does not activate the panel, just updates its content.
44124      */
44125     refresh : function(){
44126         if(this.refreshDelegate){
44127            this.loaded = false;
44128            this.refreshDelegate();
44129         }
44130     },
44131     
44132     /**
44133      * Destroys this panel
44134      */
44135     destroy : function(){
44136         this.el.removeAllListeners();
44137         var tempEl = document.createElement("span");
44138         tempEl.appendChild(this.el.dom);
44139         tempEl.innerHTML = "";
44140         this.el.remove();
44141         this.el = null;
44142     },
44143     
44144     /**
44145      * form - if the content panel contains a form - this is a reference to it.
44146      * @type {Roo.form.Form}
44147      */
44148     form : false,
44149     /**
44150      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44151      *    This contains a reference to it.
44152      * @type {Roo.View}
44153      */
44154     view : false,
44155     
44156       /**
44157      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44158      * <pre><code>
44159
44160 layout.addxtype({
44161        xtype : 'Form',
44162        items: [ .... ]
44163    }
44164 );
44165
44166 </code></pre>
44167      * @param {Object} cfg Xtype definition of item to add.
44168      */
44169     
44170     
44171     getChildContainer: function () {
44172         return this.getEl();
44173     },
44174     
44175     
44176     onScroll : function(e)
44177     {
44178         this.fireEvent('scroll', this, e);
44179     }
44180     
44181     
44182     /*
44183         var  ret = new Roo.factory(cfg);
44184         return ret;
44185         
44186         
44187         // add form..
44188         if (cfg.xtype.match(/^Form$/)) {
44189             
44190             var el;
44191             //if (this.footer) {
44192             //    el = this.footer.container.insertSibling(false, 'before');
44193             //} else {
44194                 el = this.el.createChild();
44195             //}
44196
44197             this.form = new  Roo.form.Form(cfg);
44198             
44199             
44200             if ( this.form.allItems.length) {
44201                 this.form.render(el.dom);
44202             }
44203             return this.form;
44204         }
44205         // should only have one of theses..
44206         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
44207             // views.. should not be just added - used named prop 'view''
44208             
44209             cfg.el = this.el.appendChild(document.createElement("div"));
44210             // factory?
44211             
44212             var ret = new Roo.factory(cfg);
44213              
44214              ret.render && ret.render(false, ''); // render blank..
44215             this.view = ret;
44216             return ret;
44217         }
44218         return false;
44219     }
44220     \*/
44221 });
44222  
44223 /**
44224  * @class Roo.bootstrap.panel.Grid
44225  * @extends Roo.bootstrap.panel.Content
44226  * @constructor
44227  * Create a new GridPanel.
44228  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
44229  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
44230  * @param {Object} config A the config object
44231   
44232  */
44233
44234
44235
44236 Roo.bootstrap.panel.Grid = function(config)
44237 {
44238     
44239       
44240     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44241         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
44242
44243     config.el = this.wrapper;
44244     //this.el = this.wrapper;
44245     
44246       if (config.container) {
44247         // ctor'ed from a Border/panel.grid
44248         
44249         
44250         this.wrapper.setStyle("overflow", "hidden");
44251         this.wrapper.addClass('roo-grid-container');
44252
44253     }
44254     
44255     
44256     if(config.toolbar){
44257         var tool_el = this.wrapper.createChild();    
44258         this.toolbar = Roo.factory(config.toolbar);
44259         var ti = [];
44260         if (config.toolbar.items) {
44261             ti = config.toolbar.items ;
44262             delete config.toolbar.items ;
44263         }
44264         
44265         var nitems = [];
44266         this.toolbar.render(tool_el);
44267         for(var i =0;i < ti.length;i++) {
44268           //  Roo.log(['add child', items[i]]);
44269             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44270         }
44271         this.toolbar.items = nitems;
44272         
44273         delete config.toolbar;
44274     }
44275     
44276     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
44277     config.grid.scrollBody = true;;
44278     config.grid.monitorWindowResize = false; // turn off autosizing
44279     config.grid.autoHeight = false;
44280     config.grid.autoWidth = false;
44281     
44282     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
44283     
44284     if (config.background) {
44285         // render grid on panel activation (if panel background)
44286         this.on('activate', function(gp) {
44287             if (!gp.grid.rendered) {
44288                 gp.grid.render(this.wrapper);
44289                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
44290             }
44291         });
44292             
44293     } else {
44294         this.grid.render(this.wrapper);
44295         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
44296
44297     }
44298     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
44299     // ??? needed ??? config.el = this.wrapper;
44300     
44301     
44302     
44303   
44304     // xtype created footer. - not sure if will work as we normally have to render first..
44305     if (this.footer && !this.footer.el && this.footer.xtype) {
44306         
44307         var ctr = this.grid.getView().getFooterPanel(true);
44308         this.footer.dataSource = this.grid.dataSource;
44309         this.footer = Roo.factory(this.footer, Roo);
44310         this.footer.render(ctr);
44311         
44312     }
44313     
44314     
44315     
44316     
44317      
44318 };
44319
44320 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
44321 {
44322   
44323     getId : function(){
44324         return this.grid.id;
44325     },
44326     
44327     /**
44328      * Returns the grid for this panel
44329      * @return {Roo.bootstrap.Table} 
44330      */
44331     getGrid : function(){
44332         return this.grid;    
44333     },
44334     
44335     setSize : function(width, height)
44336     {
44337      
44338         //if(!this.ignoreResize(width, height)){
44339             var grid = this.grid;
44340             var size = this.adjustForComponents(width, height);
44341             // tfoot is not a footer?
44342           
44343             
44344             var gridel = grid.getGridEl();
44345             gridel.setSize(size.width, size.height);
44346             
44347             var tbd = grid.getGridEl().select('tbody', true).first();
44348             var thd = grid.getGridEl().select('thead',true).first();
44349             var tbf= grid.getGridEl().select('tfoot', true).first();
44350
44351             if (tbf) {
44352                 size.height -= tbf.getHeight();
44353             }
44354             if (thd) {
44355                 size.height -= thd.getHeight();
44356             }
44357             
44358             tbd.setSize(size.width, size.height );
44359             // this is for the account management tab -seems to work there.
44360             var thd = grid.getGridEl().select('thead',true).first();
44361             //if (tbd) {
44362             //    tbd.setSize(size.width, size.height - thd.getHeight());
44363             //}
44364              
44365             grid.autoSize();
44366         //}
44367    
44368     },
44369      
44370     
44371     
44372     beforeSlide : function(){
44373         this.grid.getView().scroller.clip();
44374     },
44375     
44376     afterSlide : function(){
44377         this.grid.getView().scroller.unclip();
44378     },
44379     
44380     destroy : function(){
44381         this.grid.destroy();
44382         delete this.grid;
44383         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
44384     }
44385 });
44386
44387 /**
44388  * @class Roo.bootstrap.panel.Nest
44389  * @extends Roo.bootstrap.panel.Content
44390  * @constructor
44391  * Create a new Panel, that can contain a layout.Border.
44392  * 
44393  * 
44394  * @param {String/Object} config A string to set only the title or a config object
44395  */
44396 Roo.bootstrap.panel.Nest = function(config)
44397 {
44398     // construct with only one argument..
44399     /* FIXME - implement nicer consturctors
44400     if (layout.layout) {
44401         config = layout;
44402         layout = config.layout;
44403         delete config.layout;
44404     }
44405     if (layout.xtype && !layout.getEl) {
44406         // then layout needs constructing..
44407         layout = Roo.factory(layout, Roo);
44408     }
44409     */
44410     
44411     config.el =  config.layout.getEl();
44412     
44413     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
44414     
44415     config.layout.monitorWindowResize = false; // turn off autosizing
44416     this.layout = config.layout;
44417     this.layout.getEl().addClass("roo-layout-nested-layout");
44418     this.layout.parent = this;
44419     
44420     
44421     
44422     
44423 };
44424
44425 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
44426     /**
44427     * @cfg {Roo.BorderLayout} layout The layout for this panel
44428     */
44429     layout : false,
44430
44431     setSize : function(width, height){
44432         if(!this.ignoreResize(width, height)){
44433             var size = this.adjustForComponents(width, height);
44434             var el = this.layout.getEl();
44435             if (size.height < 1) {
44436                 el.setWidth(size.width);   
44437             } else {
44438                 el.setSize(size.width, size.height);
44439             }
44440             var touch = el.dom.offsetWidth;
44441             this.layout.layout();
44442             // ie requires a double layout on the first pass
44443             if(Roo.isIE && !this.initialized){
44444                 this.initialized = true;
44445                 this.layout.layout();
44446             }
44447         }
44448     },
44449     
44450     // activate all subpanels if not currently active..
44451     
44452     setActiveState : function(active){
44453         this.active = active;
44454         this.setActiveClass(active);
44455         
44456         if(!active){
44457             this.fireEvent("deactivate", this);
44458             return;
44459         }
44460         
44461         this.fireEvent("activate", this);
44462         // not sure if this should happen before or after..
44463         if (!this.layout) {
44464             return; // should not happen..
44465         }
44466         var reg = false;
44467         for (var r in this.layout.regions) {
44468             reg = this.layout.getRegion(r);
44469             if (reg.getActivePanel()) {
44470                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44471                 reg.setActivePanel(reg.getActivePanel());
44472                 continue;
44473             }
44474             if (!reg.panels.length) {
44475                 continue;
44476             }
44477             reg.showPanel(reg.getPanel(0));
44478         }
44479         
44480         
44481         
44482         
44483     },
44484     
44485     /**
44486      * Returns the nested BorderLayout for this panel
44487      * @return {Roo.BorderLayout} 
44488      */
44489     getLayout : function(){
44490         return this.layout;
44491     },
44492     
44493      /**
44494      * Adds a xtype elements to the layout of the nested panel
44495      * <pre><code>
44496
44497 panel.addxtype({
44498        xtype : 'ContentPanel',
44499        region: 'west',
44500        items: [ .... ]
44501    }
44502 );
44503
44504 panel.addxtype({
44505         xtype : 'NestedLayoutPanel',
44506         region: 'west',
44507         layout: {
44508            center: { },
44509            west: { }   
44510         },
44511         items : [ ... list of content panels or nested layout panels.. ]
44512    }
44513 );
44514 </code></pre>
44515      * @param {Object} cfg Xtype definition of item to add.
44516      */
44517     addxtype : function(cfg) {
44518         return this.layout.addxtype(cfg);
44519     
44520     }
44521 });/*
44522  * Based on:
44523  * Ext JS Library 1.1.1
44524  * Copyright(c) 2006-2007, Ext JS, LLC.
44525  *
44526  * Originally Released Under LGPL - original licence link has changed is not relivant.
44527  *
44528  * Fork - LGPL
44529  * <script type="text/javascript">
44530  */
44531 /**
44532  * @class Roo.TabPanel
44533  * @extends Roo.util.Observable
44534  * A lightweight tab container.
44535  * <br><br>
44536  * Usage:
44537  * <pre><code>
44538 // basic tabs 1, built from existing content
44539 var tabs = new Roo.TabPanel("tabs1");
44540 tabs.addTab("script", "View Script");
44541 tabs.addTab("markup", "View Markup");
44542 tabs.activate("script");
44543
44544 // more advanced tabs, built from javascript
44545 var jtabs = new Roo.TabPanel("jtabs");
44546 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44547
44548 // set up the UpdateManager
44549 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44550 var updater = tab2.getUpdateManager();
44551 updater.setDefaultUrl("ajax1.htm");
44552 tab2.on('activate', updater.refresh, updater, true);
44553
44554 // Use setUrl for Ajax loading
44555 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44556 tab3.setUrl("ajax2.htm", null, true);
44557
44558 // Disabled tab
44559 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44560 tab4.disable();
44561
44562 jtabs.activate("jtabs-1");
44563  * </code></pre>
44564  * @constructor
44565  * Create a new TabPanel.
44566  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44567  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44568  */
44569 Roo.bootstrap.panel.Tabs = function(config){
44570     /**
44571     * The container element for this TabPanel.
44572     * @type Roo.Element
44573     */
44574     this.el = Roo.get(config.el);
44575     delete config.el;
44576     if(config){
44577         if(typeof config == "boolean"){
44578             this.tabPosition = config ? "bottom" : "top";
44579         }else{
44580             Roo.apply(this, config);
44581         }
44582     }
44583     
44584     if(this.tabPosition == "bottom"){
44585         // if tabs are at the bottom = create the body first.
44586         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44587         this.el.addClass("roo-tabs-bottom");
44588     }
44589     // next create the tabs holders
44590     
44591     if (this.tabPosition == "west"){
44592         
44593         var reg = this.region; // fake it..
44594         while (reg) {
44595             if (!reg.mgr.parent) {
44596                 break;
44597             }
44598             reg = reg.mgr.parent.region;
44599         }
44600         Roo.log("got nest?");
44601         Roo.log(reg);
44602         if (reg.mgr.getRegion('west')) {
44603             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44604             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44605             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44606             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44607             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44608         
44609             
44610         }
44611         
44612         
44613     } else {
44614      
44615         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44616         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44617         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44618         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44619     }
44620     
44621     
44622     if(Roo.isIE){
44623         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44624     }
44625     
44626     // finally - if tabs are at the top, then create the body last..
44627     if(this.tabPosition != "bottom"){
44628         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44629          * @type Roo.Element
44630          */
44631         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44632         this.el.addClass("roo-tabs-top");
44633     }
44634     this.items = [];
44635
44636     this.bodyEl.setStyle("position", "relative");
44637
44638     this.active = null;
44639     this.activateDelegate = this.activate.createDelegate(this);
44640
44641     this.addEvents({
44642         /**
44643          * @event tabchange
44644          * Fires when the active tab changes
44645          * @param {Roo.TabPanel} this
44646          * @param {Roo.TabPanelItem} activePanel The new active tab
44647          */
44648         "tabchange": true,
44649         /**
44650          * @event beforetabchange
44651          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44652          * @param {Roo.TabPanel} this
44653          * @param {Object} e Set cancel to true on this object to cancel the tab change
44654          * @param {Roo.TabPanelItem} tab The tab being changed to
44655          */
44656         "beforetabchange" : true
44657     });
44658
44659     Roo.EventManager.onWindowResize(this.onResize, this);
44660     this.cpad = this.el.getPadding("lr");
44661     this.hiddenCount = 0;
44662
44663
44664     // toolbar on the tabbar support...
44665     if (this.toolbar) {
44666         alert("no toolbar support yet");
44667         this.toolbar  = false;
44668         /*
44669         var tcfg = this.toolbar;
44670         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44671         this.toolbar = new Roo.Toolbar(tcfg);
44672         if (Roo.isSafari) {
44673             var tbl = tcfg.container.child('table', true);
44674             tbl.setAttribute('width', '100%');
44675         }
44676         */
44677         
44678     }
44679    
44680
44681
44682     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44683 };
44684
44685 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44686     /*
44687      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44688      */
44689     tabPosition : "top",
44690     /*
44691      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44692      */
44693     currentTabWidth : 0,
44694     /*
44695      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44696      */
44697     minTabWidth : 40,
44698     /*
44699      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44700      */
44701     maxTabWidth : 250,
44702     /*
44703      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44704      */
44705     preferredTabWidth : 175,
44706     /*
44707      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44708      */
44709     resizeTabs : false,
44710     /*
44711      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44712      */
44713     monitorResize : true,
44714     /*
44715      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44716      */
44717     toolbar : false,  // set by caller..
44718     
44719     region : false, /// set by caller
44720     
44721     disableTooltips : true, // not used yet...
44722
44723     /**
44724      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44725      * @param {String} id The id of the div to use <b>or create</b>
44726      * @param {String} text The text for the tab
44727      * @param {String} content (optional) Content to put in the TabPanelItem body
44728      * @param {Boolean} closable (optional) True to create a close icon on the tab
44729      * @return {Roo.TabPanelItem} The created TabPanelItem
44730      */
44731     addTab : function(id, text, content, closable, tpl)
44732     {
44733         var item = new Roo.bootstrap.panel.TabItem({
44734             panel: this,
44735             id : id,
44736             text : text,
44737             closable : closable,
44738             tpl : tpl
44739         });
44740         this.addTabItem(item);
44741         if(content){
44742             item.setContent(content);
44743         }
44744         return item;
44745     },
44746
44747     /**
44748      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44749      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44750      * @return {Roo.TabPanelItem}
44751      */
44752     getTab : function(id){
44753         return this.items[id];
44754     },
44755
44756     /**
44757      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44758      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44759      */
44760     hideTab : function(id){
44761         var t = this.items[id];
44762         if(!t.isHidden()){
44763            t.setHidden(true);
44764            this.hiddenCount++;
44765            this.autoSizeTabs();
44766         }
44767     },
44768
44769     /**
44770      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44771      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44772      */
44773     unhideTab : function(id){
44774         var t = this.items[id];
44775         if(t.isHidden()){
44776            t.setHidden(false);
44777            this.hiddenCount--;
44778            this.autoSizeTabs();
44779         }
44780     },
44781
44782     /**
44783      * Adds an existing {@link Roo.TabPanelItem}.
44784      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44785      */
44786     addTabItem : function(item)
44787     {
44788         this.items[item.id] = item;
44789         this.items.push(item);
44790         this.autoSizeTabs();
44791       //  if(this.resizeTabs){
44792     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44793   //         this.autoSizeTabs();
44794 //        }else{
44795 //            item.autoSize();
44796        // }
44797     },
44798
44799     /**
44800      * Removes a {@link Roo.TabPanelItem}.
44801      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44802      */
44803     removeTab : function(id){
44804         var items = this.items;
44805         var tab = items[id];
44806         if(!tab) { return; }
44807         var index = items.indexOf(tab);
44808         if(this.active == tab && items.length > 1){
44809             var newTab = this.getNextAvailable(index);
44810             if(newTab) {
44811                 newTab.activate();
44812             }
44813         }
44814         this.stripEl.dom.removeChild(tab.pnode.dom);
44815         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44816             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44817         }
44818         items.splice(index, 1);
44819         delete this.items[tab.id];
44820         tab.fireEvent("close", tab);
44821         tab.purgeListeners();
44822         this.autoSizeTabs();
44823     },
44824
44825     getNextAvailable : function(start){
44826         var items = this.items;
44827         var index = start;
44828         // look for a next tab that will slide over to
44829         // replace the one being removed
44830         while(index < items.length){
44831             var item = items[++index];
44832             if(item && !item.isHidden()){
44833                 return item;
44834             }
44835         }
44836         // if one isn't found select the previous tab (on the left)
44837         index = start;
44838         while(index >= 0){
44839             var item = items[--index];
44840             if(item && !item.isHidden()){
44841                 return item;
44842             }
44843         }
44844         return null;
44845     },
44846
44847     /**
44848      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44849      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44850      */
44851     disableTab : function(id){
44852         var tab = this.items[id];
44853         if(tab && this.active != tab){
44854             tab.disable();
44855         }
44856     },
44857
44858     /**
44859      * Enables a {@link Roo.TabPanelItem} that is disabled.
44860      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44861      */
44862     enableTab : function(id){
44863         var tab = this.items[id];
44864         tab.enable();
44865     },
44866
44867     /**
44868      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44869      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44870      * @return {Roo.TabPanelItem} The TabPanelItem.
44871      */
44872     activate : function(id)
44873     {
44874         //Roo.log('activite:'  + id);
44875         
44876         var tab = this.items[id];
44877         if(!tab){
44878             return null;
44879         }
44880         if(tab == this.active || tab.disabled){
44881             return tab;
44882         }
44883         var e = {};
44884         this.fireEvent("beforetabchange", this, e, tab);
44885         if(e.cancel !== true && !tab.disabled){
44886             if(this.active){
44887                 this.active.hide();
44888             }
44889             this.active = this.items[id];
44890             this.active.show();
44891             this.fireEvent("tabchange", this, this.active);
44892         }
44893         return tab;
44894     },
44895
44896     /**
44897      * Gets the active {@link Roo.TabPanelItem}.
44898      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44899      */
44900     getActiveTab : function(){
44901         return this.active;
44902     },
44903
44904     /**
44905      * Updates the tab body element to fit the height of the container element
44906      * for overflow scrolling
44907      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44908      */
44909     syncHeight : function(targetHeight){
44910         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44911         var bm = this.bodyEl.getMargins();
44912         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44913         this.bodyEl.setHeight(newHeight);
44914         return newHeight;
44915     },
44916
44917     onResize : function(){
44918         if(this.monitorResize){
44919             this.autoSizeTabs();
44920         }
44921     },
44922
44923     /**
44924      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44925      */
44926     beginUpdate : function(){
44927         this.updating = true;
44928     },
44929
44930     /**
44931      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44932      */
44933     endUpdate : function(){
44934         this.updating = false;
44935         this.autoSizeTabs();
44936     },
44937
44938     /**
44939      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44940      */
44941     autoSizeTabs : function()
44942     {
44943         var count = this.items.length;
44944         var vcount = count - this.hiddenCount;
44945         
44946         if (vcount < 2) {
44947             this.stripEl.hide();
44948         } else {
44949             this.stripEl.show();
44950         }
44951         
44952         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44953             return;
44954         }
44955         
44956         
44957         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44958         var availWidth = Math.floor(w / vcount);
44959         var b = this.stripBody;
44960         if(b.getWidth() > w){
44961             var tabs = this.items;
44962             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44963             if(availWidth < this.minTabWidth){
44964                 /*if(!this.sleft){    // incomplete scrolling code
44965                     this.createScrollButtons();
44966                 }
44967                 this.showScroll();
44968                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44969             }
44970         }else{
44971             if(this.currentTabWidth < this.preferredTabWidth){
44972                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44973             }
44974         }
44975     },
44976
44977     /**
44978      * Returns the number of tabs in this TabPanel.
44979      * @return {Number}
44980      */
44981      getCount : function(){
44982          return this.items.length;
44983      },
44984
44985     /**
44986      * Resizes all the tabs to the passed width
44987      * @param {Number} The new width
44988      */
44989     setTabWidth : function(width){
44990         this.currentTabWidth = width;
44991         for(var i = 0, len = this.items.length; i < len; i++) {
44992                 if(!this.items[i].isHidden()) {
44993                 this.items[i].setWidth(width);
44994             }
44995         }
44996     },
44997
44998     /**
44999      * Destroys this TabPanel
45000      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45001      */
45002     destroy : function(removeEl){
45003         Roo.EventManager.removeResizeListener(this.onResize, this);
45004         for(var i = 0, len = this.items.length; i < len; i++){
45005             this.items[i].purgeListeners();
45006         }
45007         if(removeEl === true){
45008             this.el.update("");
45009             this.el.remove();
45010         }
45011     },
45012     
45013     createStrip : function(container)
45014     {
45015         var strip = document.createElement("nav");
45016         strip.className = Roo.bootstrap.version == 4 ?
45017             "navbar-light bg-light" : 
45018             "navbar navbar-default"; //"x-tabs-wrap";
45019         container.appendChild(strip);
45020         return strip;
45021     },
45022     
45023     createStripList : function(strip)
45024     {
45025         // div wrapper for retard IE
45026         // returns the "tr" element.
45027         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45028         //'<div class="x-tabs-strip-wrap">'+
45029           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45030           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45031         return strip.firstChild; //.firstChild.firstChild.firstChild;
45032     },
45033     createBody : function(container)
45034     {
45035         var body = document.createElement("div");
45036         Roo.id(body, "tab-body");
45037         //Roo.fly(body).addClass("x-tabs-body");
45038         Roo.fly(body).addClass("tab-content");
45039         container.appendChild(body);
45040         return body;
45041     },
45042     createItemBody :function(bodyEl, id){
45043         var body = Roo.getDom(id);
45044         if(!body){
45045             body = document.createElement("div");
45046             body.id = id;
45047         }
45048         //Roo.fly(body).addClass("x-tabs-item-body");
45049         Roo.fly(body).addClass("tab-pane");
45050          bodyEl.insertBefore(body, bodyEl.firstChild);
45051         return body;
45052     },
45053     /** @private */
45054     createStripElements :  function(stripEl, text, closable, tpl)
45055     {
45056         var td = document.createElement("li"); // was td..
45057         td.className = 'nav-item';
45058         
45059         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45060         
45061         
45062         stripEl.appendChild(td);
45063         /*if(closable){
45064             td.className = "x-tabs-closable";
45065             if(!this.closeTpl){
45066                 this.closeTpl = new Roo.Template(
45067                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45068                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45069                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45070                 );
45071             }
45072             var el = this.closeTpl.overwrite(td, {"text": text});
45073             var close = el.getElementsByTagName("div")[0];
45074             var inner = el.getElementsByTagName("em")[0];
45075             return {"el": el, "close": close, "inner": inner};
45076         } else {
45077         */
45078         // not sure what this is..
45079 //            if(!this.tabTpl){
45080                 //this.tabTpl = new Roo.Template(
45081                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45082                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45083                 //);
45084 //                this.tabTpl = new Roo.Template(
45085 //                   '<a href="#">' +
45086 //                   '<span unselectable="on"' +
45087 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45088 //                            ' >{text}</span></a>'
45089 //                );
45090 //                
45091 //            }
45092
45093
45094             var template = tpl || this.tabTpl || false;
45095             
45096             if(!template){
45097                 template =  new Roo.Template(
45098                         Roo.bootstrap.version == 4 ? 
45099                             (
45100                                 '<a class="nav-link" href="#" unselectable="on"' +
45101                                      (this.disableTooltips ? '' : ' title="{text}"') +
45102                                      ' >{text}</a>'
45103                             ) : (
45104                                 '<a class="nav-link" href="#">' +
45105                                 '<span unselectable="on"' +
45106                                          (this.disableTooltips ? '' : ' title="{text}"') +
45107                                     ' >{text}</span></a>'
45108                             )
45109                 );
45110             }
45111             
45112             switch (typeof(template)) {
45113                 case 'object' :
45114                     break;
45115                 case 'string' :
45116                     template = new Roo.Template(template);
45117                     break;
45118                 default :
45119                     break;
45120             }
45121             
45122             var el = template.overwrite(td, {"text": text});
45123             
45124             var inner = el.getElementsByTagName("span")[0];
45125             
45126             return {"el": el, "inner": inner};
45127             
45128     }
45129         
45130     
45131 });
45132
45133 /**
45134  * @class Roo.TabPanelItem
45135  * @extends Roo.util.Observable
45136  * Represents an individual item (tab plus body) in a TabPanel.
45137  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45138  * @param {String} id The id of this TabPanelItem
45139  * @param {String} text The text for the tab of this TabPanelItem
45140  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45141  */
45142 Roo.bootstrap.panel.TabItem = function(config){
45143     /**
45144      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45145      * @type Roo.TabPanel
45146      */
45147     this.tabPanel = config.panel;
45148     /**
45149      * The id for this TabPanelItem
45150      * @type String
45151      */
45152     this.id = config.id;
45153     /** @private */
45154     this.disabled = false;
45155     /** @private */
45156     this.text = config.text;
45157     /** @private */
45158     this.loaded = false;
45159     this.closable = config.closable;
45160
45161     /**
45162      * The body element for this TabPanelItem.
45163      * @type Roo.Element
45164      */
45165     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45166     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45167     this.bodyEl.setStyle("display", "block");
45168     this.bodyEl.setStyle("zoom", "1");
45169     //this.hideAction();
45170
45171     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45172     /** @private */
45173     this.el = Roo.get(els.el);
45174     this.inner = Roo.get(els.inner, true);
45175      this.textEl = Roo.bootstrap.version == 4 ?
45176         this.el : Roo.get(this.el.dom.firstChild, true);
45177
45178     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45179     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45180
45181     
45182 //    this.el.on("mousedown", this.onTabMouseDown, this);
45183     this.el.on("click", this.onTabClick, this);
45184     /** @private */
45185     if(config.closable){
45186         var c = Roo.get(els.close, true);
45187         c.dom.title = this.closeText;
45188         c.addClassOnOver("close-over");
45189         c.on("click", this.closeClick, this);
45190      }
45191
45192     this.addEvents({
45193          /**
45194          * @event activate
45195          * Fires when this tab becomes the active tab.
45196          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45197          * @param {Roo.TabPanelItem} this
45198          */
45199         "activate": true,
45200         /**
45201          * @event beforeclose
45202          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
45203          * @param {Roo.TabPanelItem} this
45204          * @param {Object} e Set cancel to true on this object to cancel the close.
45205          */
45206         "beforeclose": true,
45207         /**
45208          * @event close
45209          * Fires when this tab is closed.
45210          * @param {Roo.TabPanelItem} this
45211          */
45212          "close": true,
45213         /**
45214          * @event deactivate
45215          * Fires when this tab is no longer the active tab.
45216          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45217          * @param {Roo.TabPanelItem} this
45218          */
45219          "deactivate" : true
45220     });
45221     this.hidden = false;
45222
45223     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
45224 };
45225
45226 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
45227            {
45228     purgeListeners : function(){
45229        Roo.util.Observable.prototype.purgeListeners.call(this);
45230        this.el.removeAllListeners();
45231     },
45232     /**
45233      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
45234      */
45235     show : function(){
45236         this.status_node.addClass("active");
45237         this.showAction();
45238         if(Roo.isOpera){
45239             this.tabPanel.stripWrap.repaint();
45240         }
45241         this.fireEvent("activate", this.tabPanel, this);
45242     },
45243
45244     /**
45245      * Returns true if this tab is the active tab.
45246      * @return {Boolean}
45247      */
45248     isActive : function(){
45249         return this.tabPanel.getActiveTab() == this;
45250     },
45251
45252     /**
45253      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
45254      */
45255     hide : function(){
45256         this.status_node.removeClass("active");
45257         this.hideAction();
45258         this.fireEvent("deactivate", this.tabPanel, this);
45259     },
45260
45261     hideAction : function(){
45262         this.bodyEl.hide();
45263         this.bodyEl.setStyle("position", "absolute");
45264         this.bodyEl.setLeft("-20000px");
45265         this.bodyEl.setTop("-20000px");
45266     },
45267
45268     showAction : function(){
45269         this.bodyEl.setStyle("position", "relative");
45270         this.bodyEl.setTop("");
45271         this.bodyEl.setLeft("");
45272         this.bodyEl.show();
45273     },
45274
45275     /**
45276      * Set the tooltip for the tab.
45277      * @param {String} tooltip The tab's tooltip
45278      */
45279     setTooltip : function(text){
45280         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
45281             this.textEl.dom.qtip = text;
45282             this.textEl.dom.removeAttribute('title');
45283         }else{
45284             this.textEl.dom.title = text;
45285         }
45286     },
45287
45288     onTabClick : function(e){
45289         e.preventDefault();
45290         this.tabPanel.activate(this.id);
45291     },
45292
45293     onTabMouseDown : function(e){
45294         e.preventDefault();
45295         this.tabPanel.activate(this.id);
45296     },
45297 /*
45298     getWidth : function(){
45299         return this.inner.getWidth();
45300     },
45301
45302     setWidth : function(width){
45303         var iwidth = width - this.linode.getPadding("lr");
45304         this.inner.setWidth(iwidth);
45305         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
45306         this.linode.setWidth(width);
45307     },
45308 */
45309     /**
45310      * Show or hide the tab
45311      * @param {Boolean} hidden True to hide or false to show.
45312      */
45313     setHidden : function(hidden){
45314         this.hidden = hidden;
45315         this.linode.setStyle("display", hidden ? "none" : "");
45316     },
45317
45318     /**
45319      * Returns true if this tab is "hidden"
45320      * @return {Boolean}
45321      */
45322     isHidden : function(){
45323         return this.hidden;
45324     },
45325
45326     /**
45327      * Returns the text for this tab
45328      * @return {String}
45329      */
45330     getText : function(){
45331         return this.text;
45332     },
45333     /*
45334     autoSize : function(){
45335         //this.el.beginMeasure();
45336         this.textEl.setWidth(1);
45337         /*
45338          *  #2804 [new] Tabs in Roojs
45339          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
45340          */
45341         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
45342         //this.el.endMeasure();
45343     //},
45344
45345     /**
45346      * Sets the text for the tab (Note: this also sets the tooltip text)
45347      * @param {String} text The tab's text and tooltip
45348      */
45349     setText : function(text){
45350         this.text = text;
45351         this.textEl.update(text);
45352         this.setTooltip(text);
45353         //if(!this.tabPanel.resizeTabs){
45354         //    this.autoSize();
45355         //}
45356     },
45357     /**
45358      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
45359      */
45360     activate : function(){
45361         this.tabPanel.activate(this.id);
45362     },
45363
45364     /**
45365      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
45366      */
45367     disable : function(){
45368         if(this.tabPanel.active != this){
45369             this.disabled = true;
45370             this.status_node.addClass("disabled");
45371         }
45372     },
45373
45374     /**
45375      * Enables this TabPanelItem if it was previously disabled.
45376      */
45377     enable : function(){
45378         this.disabled = false;
45379         this.status_node.removeClass("disabled");
45380     },
45381
45382     /**
45383      * Sets the content for this TabPanelItem.
45384      * @param {String} content The content
45385      * @param {Boolean} loadScripts true to look for and load scripts
45386      */
45387     setContent : function(content, loadScripts){
45388         this.bodyEl.update(content, loadScripts);
45389     },
45390
45391     /**
45392      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
45393      * @return {Roo.UpdateManager} The UpdateManager
45394      */
45395     getUpdateManager : function(){
45396         return this.bodyEl.getUpdateManager();
45397     },
45398
45399     /**
45400      * Set a URL to be used to load the content for this TabPanelItem.
45401      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
45402      * @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)
45403      * @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)
45404      * @return {Roo.UpdateManager} The UpdateManager
45405      */
45406     setUrl : function(url, params, loadOnce){
45407         if(this.refreshDelegate){
45408             this.un('activate', this.refreshDelegate);
45409         }
45410         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45411         this.on("activate", this.refreshDelegate);
45412         return this.bodyEl.getUpdateManager();
45413     },
45414
45415     /** @private */
45416     _handleRefresh : function(url, params, loadOnce){
45417         if(!loadOnce || !this.loaded){
45418             var updater = this.bodyEl.getUpdateManager();
45419             updater.update(url, params, this._setLoaded.createDelegate(this));
45420         }
45421     },
45422
45423     /**
45424      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
45425      *   Will fail silently if the setUrl method has not been called.
45426      *   This does not activate the panel, just updates its content.
45427      */
45428     refresh : function(){
45429         if(this.refreshDelegate){
45430            this.loaded = false;
45431            this.refreshDelegate();
45432         }
45433     },
45434
45435     /** @private */
45436     _setLoaded : function(){
45437         this.loaded = true;
45438     },
45439
45440     /** @private */
45441     closeClick : function(e){
45442         var o = {};
45443         e.stopEvent();
45444         this.fireEvent("beforeclose", this, o);
45445         if(o.cancel !== true){
45446             this.tabPanel.removeTab(this.id);
45447         }
45448     },
45449     /**
45450      * The text displayed in the tooltip for the close icon.
45451      * @type String
45452      */
45453     closeText : "Close this tab"
45454 });
45455 /**
45456 *    This script refer to:
45457 *    Title: International Telephone Input
45458 *    Author: Jack O'Connor
45459 *    Code version:  v12.1.12
45460 *    Availability: https://github.com/jackocnr/intl-tel-input.git
45461 **/
45462
45463 Roo.bootstrap.form.PhoneInputData = function() {
45464     var d = [
45465       [
45466         "Afghanistan (‫افغانستان‬‎)",
45467         "af",
45468         "93"
45469       ],
45470       [
45471         "Albania (Shqipëri)",
45472         "al",
45473         "355"
45474       ],
45475       [
45476         "Algeria (‫الجزائر‬‎)",
45477         "dz",
45478         "213"
45479       ],
45480       [
45481         "American Samoa",
45482         "as",
45483         "1684"
45484       ],
45485       [
45486         "Andorra",
45487         "ad",
45488         "376"
45489       ],
45490       [
45491         "Angola",
45492         "ao",
45493         "244"
45494       ],
45495       [
45496         "Anguilla",
45497         "ai",
45498         "1264"
45499       ],
45500       [
45501         "Antigua and Barbuda",
45502         "ag",
45503         "1268"
45504       ],
45505       [
45506         "Argentina",
45507         "ar",
45508         "54"
45509       ],
45510       [
45511         "Armenia (Հայաստան)",
45512         "am",
45513         "374"
45514       ],
45515       [
45516         "Aruba",
45517         "aw",
45518         "297"
45519       ],
45520       [
45521         "Australia",
45522         "au",
45523         "61",
45524         0
45525       ],
45526       [
45527         "Austria (Österreich)",
45528         "at",
45529         "43"
45530       ],
45531       [
45532         "Azerbaijan (Azərbaycan)",
45533         "az",
45534         "994"
45535       ],
45536       [
45537         "Bahamas",
45538         "bs",
45539         "1242"
45540       ],
45541       [
45542         "Bahrain (‫البحرين‬‎)",
45543         "bh",
45544         "973"
45545       ],
45546       [
45547         "Bangladesh (বাংলাদেশ)",
45548         "bd",
45549         "880"
45550       ],
45551       [
45552         "Barbados",
45553         "bb",
45554         "1246"
45555       ],
45556       [
45557         "Belarus (Беларусь)",
45558         "by",
45559         "375"
45560       ],
45561       [
45562         "Belgium (België)",
45563         "be",
45564         "32"
45565       ],
45566       [
45567         "Belize",
45568         "bz",
45569         "501"
45570       ],
45571       [
45572         "Benin (Bénin)",
45573         "bj",
45574         "229"
45575       ],
45576       [
45577         "Bermuda",
45578         "bm",
45579         "1441"
45580       ],
45581       [
45582         "Bhutan (འབྲུག)",
45583         "bt",
45584         "975"
45585       ],
45586       [
45587         "Bolivia",
45588         "bo",
45589         "591"
45590       ],
45591       [
45592         "Bosnia and Herzegovina (Босна и Херцеговина)",
45593         "ba",
45594         "387"
45595       ],
45596       [
45597         "Botswana",
45598         "bw",
45599         "267"
45600       ],
45601       [
45602         "Brazil (Brasil)",
45603         "br",
45604         "55"
45605       ],
45606       [
45607         "British Indian Ocean Territory",
45608         "io",
45609         "246"
45610       ],
45611       [
45612         "British Virgin Islands",
45613         "vg",
45614         "1284"
45615       ],
45616       [
45617         "Brunei",
45618         "bn",
45619         "673"
45620       ],
45621       [
45622         "Bulgaria (България)",
45623         "bg",
45624         "359"
45625       ],
45626       [
45627         "Burkina Faso",
45628         "bf",
45629         "226"
45630       ],
45631       [
45632         "Burundi (Uburundi)",
45633         "bi",
45634         "257"
45635       ],
45636       [
45637         "Cambodia (កម្ពុជា)",
45638         "kh",
45639         "855"
45640       ],
45641       [
45642         "Cameroon (Cameroun)",
45643         "cm",
45644         "237"
45645       ],
45646       [
45647         "Canada",
45648         "ca",
45649         "1",
45650         1,
45651         ["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"]
45652       ],
45653       [
45654         "Cape Verde (Kabu Verdi)",
45655         "cv",
45656         "238"
45657       ],
45658       [
45659         "Caribbean Netherlands",
45660         "bq",
45661         "599",
45662         1
45663       ],
45664       [
45665         "Cayman Islands",
45666         "ky",
45667         "1345"
45668       ],
45669       [
45670         "Central African Republic (République centrafricaine)",
45671         "cf",
45672         "236"
45673       ],
45674       [
45675         "Chad (Tchad)",
45676         "td",
45677         "235"
45678       ],
45679       [
45680         "Chile",
45681         "cl",
45682         "56"
45683       ],
45684       [
45685         "China (中国)",
45686         "cn",
45687         "86"
45688       ],
45689       [
45690         "Christmas Island",
45691         "cx",
45692         "61",
45693         2
45694       ],
45695       [
45696         "Cocos (Keeling) Islands",
45697         "cc",
45698         "61",
45699         1
45700       ],
45701       [
45702         "Colombia",
45703         "co",
45704         "57"
45705       ],
45706       [
45707         "Comoros (‫جزر القمر‬‎)",
45708         "km",
45709         "269"
45710       ],
45711       [
45712         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45713         "cd",
45714         "243"
45715       ],
45716       [
45717         "Congo (Republic) (Congo-Brazzaville)",
45718         "cg",
45719         "242"
45720       ],
45721       [
45722         "Cook Islands",
45723         "ck",
45724         "682"
45725       ],
45726       [
45727         "Costa Rica",
45728         "cr",
45729         "506"
45730       ],
45731       [
45732         "Côte d’Ivoire",
45733         "ci",
45734         "225"
45735       ],
45736       [
45737         "Croatia (Hrvatska)",
45738         "hr",
45739         "385"
45740       ],
45741       [
45742         "Cuba",
45743         "cu",
45744         "53"
45745       ],
45746       [
45747         "Curaçao",
45748         "cw",
45749         "599",
45750         0
45751       ],
45752       [
45753         "Cyprus (Κύπρος)",
45754         "cy",
45755         "357"
45756       ],
45757       [
45758         "Czech Republic (Česká republika)",
45759         "cz",
45760         "420"
45761       ],
45762       [
45763         "Denmark (Danmark)",
45764         "dk",
45765         "45"
45766       ],
45767       [
45768         "Djibouti",
45769         "dj",
45770         "253"
45771       ],
45772       [
45773         "Dominica",
45774         "dm",
45775         "1767"
45776       ],
45777       [
45778         "Dominican Republic (República Dominicana)",
45779         "do",
45780         "1",
45781         2,
45782         ["809", "829", "849"]
45783       ],
45784       [
45785         "Ecuador",
45786         "ec",
45787         "593"
45788       ],
45789       [
45790         "Egypt (‫مصر‬‎)",
45791         "eg",
45792         "20"
45793       ],
45794       [
45795         "El Salvador",
45796         "sv",
45797         "503"
45798       ],
45799       [
45800         "Equatorial Guinea (Guinea Ecuatorial)",
45801         "gq",
45802         "240"
45803       ],
45804       [
45805         "Eritrea",
45806         "er",
45807         "291"
45808       ],
45809       [
45810         "Estonia (Eesti)",
45811         "ee",
45812         "372"
45813       ],
45814       [
45815         "Ethiopia",
45816         "et",
45817         "251"
45818       ],
45819       [
45820         "Falkland Islands (Islas Malvinas)",
45821         "fk",
45822         "500"
45823       ],
45824       [
45825         "Faroe Islands (Føroyar)",
45826         "fo",
45827         "298"
45828       ],
45829       [
45830         "Fiji",
45831         "fj",
45832         "679"
45833       ],
45834       [
45835         "Finland (Suomi)",
45836         "fi",
45837         "358",
45838         0
45839       ],
45840       [
45841         "France",
45842         "fr",
45843         "33"
45844       ],
45845       [
45846         "French Guiana (Guyane française)",
45847         "gf",
45848         "594"
45849       ],
45850       [
45851         "French Polynesia (Polynésie française)",
45852         "pf",
45853         "689"
45854       ],
45855       [
45856         "Gabon",
45857         "ga",
45858         "241"
45859       ],
45860       [
45861         "Gambia",
45862         "gm",
45863         "220"
45864       ],
45865       [
45866         "Georgia (საქართველო)",
45867         "ge",
45868         "995"
45869       ],
45870       [
45871         "Germany (Deutschland)",
45872         "de",
45873         "49"
45874       ],
45875       [
45876         "Ghana (Gaana)",
45877         "gh",
45878         "233"
45879       ],
45880       [
45881         "Gibraltar",
45882         "gi",
45883         "350"
45884       ],
45885       [
45886         "Greece (Ελλάδα)",
45887         "gr",
45888         "30"
45889       ],
45890       [
45891         "Greenland (Kalaallit Nunaat)",
45892         "gl",
45893         "299"
45894       ],
45895       [
45896         "Grenada",
45897         "gd",
45898         "1473"
45899       ],
45900       [
45901         "Guadeloupe",
45902         "gp",
45903         "590",
45904         0
45905       ],
45906       [
45907         "Guam",
45908         "gu",
45909         "1671"
45910       ],
45911       [
45912         "Guatemala",
45913         "gt",
45914         "502"
45915       ],
45916       [
45917         "Guernsey",
45918         "gg",
45919         "44",
45920         1
45921       ],
45922       [
45923         "Guinea (Guinée)",
45924         "gn",
45925         "224"
45926       ],
45927       [
45928         "Guinea-Bissau (Guiné Bissau)",
45929         "gw",
45930         "245"
45931       ],
45932       [
45933         "Guyana",
45934         "gy",
45935         "592"
45936       ],
45937       [
45938         "Haiti",
45939         "ht",
45940         "509"
45941       ],
45942       [
45943         "Honduras",
45944         "hn",
45945         "504"
45946       ],
45947       [
45948         "Hong Kong (香港)",
45949         "hk",
45950         "852"
45951       ],
45952       [
45953         "Hungary (Magyarország)",
45954         "hu",
45955         "36"
45956       ],
45957       [
45958         "Iceland (Ísland)",
45959         "is",
45960         "354"
45961       ],
45962       [
45963         "India (भारत)",
45964         "in",
45965         "91"
45966       ],
45967       [
45968         "Indonesia",
45969         "id",
45970         "62"
45971       ],
45972       [
45973         "Iran (‫ایران‬‎)",
45974         "ir",
45975         "98"
45976       ],
45977       [
45978         "Iraq (‫العراق‬‎)",
45979         "iq",
45980         "964"
45981       ],
45982       [
45983         "Ireland",
45984         "ie",
45985         "353"
45986       ],
45987       [
45988         "Isle of Man",
45989         "im",
45990         "44",
45991         2
45992       ],
45993       [
45994         "Israel (‫ישראל‬‎)",
45995         "il",
45996         "972"
45997       ],
45998       [
45999         "Italy (Italia)",
46000         "it",
46001         "39",
46002         0
46003       ],
46004       [
46005         "Jamaica",
46006         "jm",
46007         "1876"
46008       ],
46009       [
46010         "Japan (日本)",
46011         "jp",
46012         "81"
46013       ],
46014       [
46015         "Jersey",
46016         "je",
46017         "44",
46018         3
46019       ],
46020       [
46021         "Jordan (‫الأردن‬‎)",
46022         "jo",
46023         "962"
46024       ],
46025       [
46026         "Kazakhstan (Казахстан)",
46027         "kz",
46028         "7",
46029         1
46030       ],
46031       [
46032         "Kenya",
46033         "ke",
46034         "254"
46035       ],
46036       [
46037         "Kiribati",
46038         "ki",
46039         "686"
46040       ],
46041       [
46042         "Kosovo",
46043         "xk",
46044         "383"
46045       ],
46046       [
46047         "Kuwait (‫الكويت‬‎)",
46048         "kw",
46049         "965"
46050       ],
46051       [
46052         "Kyrgyzstan (Кыргызстан)",
46053         "kg",
46054         "996"
46055       ],
46056       [
46057         "Laos (ລາວ)",
46058         "la",
46059         "856"
46060       ],
46061       [
46062         "Latvia (Latvija)",
46063         "lv",
46064         "371"
46065       ],
46066       [
46067         "Lebanon (‫لبنان‬‎)",
46068         "lb",
46069         "961"
46070       ],
46071       [
46072         "Lesotho",
46073         "ls",
46074         "266"
46075       ],
46076       [
46077         "Liberia",
46078         "lr",
46079         "231"
46080       ],
46081       [
46082         "Libya (‫ليبيا‬‎)",
46083         "ly",
46084         "218"
46085       ],
46086       [
46087         "Liechtenstein",
46088         "li",
46089         "423"
46090       ],
46091       [
46092         "Lithuania (Lietuva)",
46093         "lt",
46094         "370"
46095       ],
46096       [
46097         "Luxembourg",
46098         "lu",
46099         "352"
46100       ],
46101       [
46102         "Macau (澳門)",
46103         "mo",
46104         "853"
46105       ],
46106       [
46107         "Macedonia (FYROM) (Македонија)",
46108         "mk",
46109         "389"
46110       ],
46111       [
46112         "Madagascar (Madagasikara)",
46113         "mg",
46114         "261"
46115       ],
46116       [
46117         "Malawi",
46118         "mw",
46119         "265"
46120       ],
46121       [
46122         "Malaysia",
46123         "my",
46124         "60"
46125       ],
46126       [
46127         "Maldives",
46128         "mv",
46129         "960"
46130       ],
46131       [
46132         "Mali",
46133         "ml",
46134         "223"
46135       ],
46136       [
46137         "Malta",
46138         "mt",
46139         "356"
46140       ],
46141       [
46142         "Marshall Islands",
46143         "mh",
46144         "692"
46145       ],
46146       [
46147         "Martinique",
46148         "mq",
46149         "596"
46150       ],
46151       [
46152         "Mauritania (‫موريتانيا‬‎)",
46153         "mr",
46154         "222"
46155       ],
46156       [
46157         "Mauritius (Moris)",
46158         "mu",
46159         "230"
46160       ],
46161       [
46162         "Mayotte",
46163         "yt",
46164         "262",
46165         1
46166       ],
46167       [
46168         "Mexico (México)",
46169         "mx",
46170         "52"
46171       ],
46172       [
46173         "Micronesia",
46174         "fm",
46175         "691"
46176       ],
46177       [
46178         "Moldova (Republica Moldova)",
46179         "md",
46180         "373"
46181       ],
46182       [
46183         "Monaco",
46184         "mc",
46185         "377"
46186       ],
46187       [
46188         "Mongolia (Монгол)",
46189         "mn",
46190         "976"
46191       ],
46192       [
46193         "Montenegro (Crna Gora)",
46194         "me",
46195         "382"
46196       ],
46197       [
46198         "Montserrat",
46199         "ms",
46200         "1664"
46201       ],
46202       [
46203         "Morocco (‫المغرب‬‎)",
46204         "ma",
46205         "212",
46206         0
46207       ],
46208       [
46209         "Mozambique (Moçambique)",
46210         "mz",
46211         "258"
46212       ],
46213       [
46214         "Myanmar (Burma) (မြန်မာ)",
46215         "mm",
46216         "95"
46217       ],
46218       [
46219         "Namibia (Namibië)",
46220         "na",
46221         "264"
46222       ],
46223       [
46224         "Nauru",
46225         "nr",
46226         "674"
46227       ],
46228       [
46229         "Nepal (नेपाल)",
46230         "np",
46231         "977"
46232       ],
46233       [
46234         "Netherlands (Nederland)",
46235         "nl",
46236         "31"
46237       ],
46238       [
46239         "New Caledonia (Nouvelle-Calédonie)",
46240         "nc",
46241         "687"
46242       ],
46243       [
46244         "New Zealand",
46245         "nz",
46246         "64"
46247       ],
46248       [
46249         "Nicaragua",
46250         "ni",
46251         "505"
46252       ],
46253       [
46254         "Niger (Nijar)",
46255         "ne",
46256         "227"
46257       ],
46258       [
46259         "Nigeria",
46260         "ng",
46261         "234"
46262       ],
46263       [
46264         "Niue",
46265         "nu",
46266         "683"
46267       ],
46268       [
46269         "Norfolk Island",
46270         "nf",
46271         "672"
46272       ],
46273       [
46274         "North Korea (조선 민주주의 인민 공화국)",
46275         "kp",
46276         "850"
46277       ],
46278       [
46279         "Northern Mariana Islands",
46280         "mp",
46281         "1670"
46282       ],
46283       [
46284         "Norway (Norge)",
46285         "no",
46286         "47",
46287         0
46288       ],
46289       [
46290         "Oman (‫عُمان‬‎)",
46291         "om",
46292         "968"
46293       ],
46294       [
46295         "Pakistan (‫پاکستان‬‎)",
46296         "pk",
46297         "92"
46298       ],
46299       [
46300         "Palau",
46301         "pw",
46302         "680"
46303       ],
46304       [
46305         "Palestine (‫فلسطين‬‎)",
46306         "ps",
46307         "970"
46308       ],
46309       [
46310         "Panama (Panamá)",
46311         "pa",
46312         "507"
46313       ],
46314       [
46315         "Papua New Guinea",
46316         "pg",
46317         "675"
46318       ],
46319       [
46320         "Paraguay",
46321         "py",
46322         "595"
46323       ],
46324       [
46325         "Peru (Perú)",
46326         "pe",
46327         "51"
46328       ],
46329       [
46330         "Philippines",
46331         "ph",
46332         "63"
46333       ],
46334       [
46335         "Poland (Polska)",
46336         "pl",
46337         "48"
46338       ],
46339       [
46340         "Portugal",
46341         "pt",
46342         "351"
46343       ],
46344       [
46345         "Puerto Rico",
46346         "pr",
46347         "1",
46348         3,
46349         ["787", "939"]
46350       ],
46351       [
46352         "Qatar (‫قطر‬‎)",
46353         "qa",
46354         "974"
46355       ],
46356       [
46357         "Réunion (La Réunion)",
46358         "re",
46359         "262",
46360         0
46361       ],
46362       [
46363         "Romania (România)",
46364         "ro",
46365         "40"
46366       ],
46367       [
46368         "Russia (Россия)",
46369         "ru",
46370         "7",
46371         0
46372       ],
46373       [
46374         "Rwanda",
46375         "rw",
46376         "250"
46377       ],
46378       [
46379         "Saint Barthélemy",
46380         "bl",
46381         "590",
46382         1
46383       ],
46384       [
46385         "Saint Helena",
46386         "sh",
46387         "290"
46388       ],
46389       [
46390         "Saint Kitts and Nevis",
46391         "kn",
46392         "1869"
46393       ],
46394       [
46395         "Saint Lucia",
46396         "lc",
46397         "1758"
46398       ],
46399       [
46400         "Saint Martin (Saint-Martin (partie française))",
46401         "mf",
46402         "590",
46403         2
46404       ],
46405       [
46406         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
46407         "pm",
46408         "508"
46409       ],
46410       [
46411         "Saint Vincent and the Grenadines",
46412         "vc",
46413         "1784"
46414       ],
46415       [
46416         "Samoa",
46417         "ws",
46418         "685"
46419       ],
46420       [
46421         "San Marino",
46422         "sm",
46423         "378"
46424       ],
46425       [
46426         "São Tomé and Príncipe (São Tomé e Príncipe)",
46427         "st",
46428         "239"
46429       ],
46430       [
46431         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
46432         "sa",
46433         "966"
46434       ],
46435       [
46436         "Senegal (Sénégal)",
46437         "sn",
46438         "221"
46439       ],
46440       [
46441         "Serbia (Србија)",
46442         "rs",
46443         "381"
46444       ],
46445       [
46446         "Seychelles",
46447         "sc",
46448         "248"
46449       ],
46450       [
46451         "Sierra Leone",
46452         "sl",
46453         "232"
46454       ],
46455       [
46456         "Singapore",
46457         "sg",
46458         "65"
46459       ],
46460       [
46461         "Sint Maarten",
46462         "sx",
46463         "1721"
46464       ],
46465       [
46466         "Slovakia (Slovensko)",
46467         "sk",
46468         "421"
46469       ],
46470       [
46471         "Slovenia (Slovenija)",
46472         "si",
46473         "386"
46474       ],
46475       [
46476         "Solomon Islands",
46477         "sb",
46478         "677"
46479       ],
46480       [
46481         "Somalia (Soomaaliya)",
46482         "so",
46483         "252"
46484       ],
46485       [
46486         "South Africa",
46487         "za",
46488         "27"
46489       ],
46490       [
46491         "South Korea (대한민국)",
46492         "kr",
46493         "82"
46494       ],
46495       [
46496         "South Sudan (‫جنوب السودان‬‎)",
46497         "ss",
46498         "211"
46499       ],
46500       [
46501         "Spain (España)",
46502         "es",
46503         "34"
46504       ],
46505       [
46506         "Sri Lanka (ශ්‍රී ලංකාව)",
46507         "lk",
46508         "94"
46509       ],
46510       [
46511         "Sudan (‫السودان‬‎)",
46512         "sd",
46513         "249"
46514       ],
46515       [
46516         "Suriname",
46517         "sr",
46518         "597"
46519       ],
46520       [
46521         "Svalbard and Jan Mayen",
46522         "sj",
46523         "47",
46524         1
46525       ],
46526       [
46527         "Swaziland",
46528         "sz",
46529         "268"
46530       ],
46531       [
46532         "Sweden (Sverige)",
46533         "se",
46534         "46"
46535       ],
46536       [
46537         "Switzerland (Schweiz)",
46538         "ch",
46539         "41"
46540       ],
46541       [
46542         "Syria (‫سوريا‬‎)",
46543         "sy",
46544         "963"
46545       ],
46546       [
46547         "Taiwan (台灣)",
46548         "tw",
46549         "886"
46550       ],
46551       [
46552         "Tajikistan",
46553         "tj",
46554         "992"
46555       ],
46556       [
46557         "Tanzania",
46558         "tz",
46559         "255"
46560       ],
46561       [
46562         "Thailand (ไทย)",
46563         "th",
46564         "66"
46565       ],
46566       [
46567         "Timor-Leste",
46568         "tl",
46569         "670"
46570       ],
46571       [
46572         "Togo",
46573         "tg",
46574         "228"
46575       ],
46576       [
46577         "Tokelau",
46578         "tk",
46579         "690"
46580       ],
46581       [
46582         "Tonga",
46583         "to",
46584         "676"
46585       ],
46586       [
46587         "Trinidad and Tobago",
46588         "tt",
46589         "1868"
46590       ],
46591       [
46592         "Tunisia (‫تونس‬‎)",
46593         "tn",
46594         "216"
46595       ],
46596       [
46597         "Turkey (Türkiye)",
46598         "tr",
46599         "90"
46600       ],
46601       [
46602         "Turkmenistan",
46603         "tm",
46604         "993"
46605       ],
46606       [
46607         "Turks and Caicos Islands",
46608         "tc",
46609         "1649"
46610       ],
46611       [
46612         "Tuvalu",
46613         "tv",
46614         "688"
46615       ],
46616       [
46617         "U.S. Virgin Islands",
46618         "vi",
46619         "1340"
46620       ],
46621       [
46622         "Uganda",
46623         "ug",
46624         "256"
46625       ],
46626       [
46627         "Ukraine (Україна)",
46628         "ua",
46629         "380"
46630       ],
46631       [
46632         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46633         "ae",
46634         "971"
46635       ],
46636       [
46637         "United Kingdom",
46638         "gb",
46639         "44",
46640         0
46641       ],
46642       [
46643         "United States",
46644         "us",
46645         "1",
46646         0
46647       ],
46648       [
46649         "Uruguay",
46650         "uy",
46651         "598"
46652       ],
46653       [
46654         "Uzbekistan (Oʻzbekiston)",
46655         "uz",
46656         "998"
46657       ],
46658       [
46659         "Vanuatu",
46660         "vu",
46661         "678"
46662       ],
46663       [
46664         "Vatican City (Città del Vaticano)",
46665         "va",
46666         "39",
46667         1
46668       ],
46669       [
46670         "Venezuela",
46671         "ve",
46672         "58"
46673       ],
46674       [
46675         "Vietnam (Việt Nam)",
46676         "vn",
46677         "84"
46678       ],
46679       [
46680         "Wallis and Futuna (Wallis-et-Futuna)",
46681         "wf",
46682         "681"
46683       ],
46684       [
46685         "Western Sahara (‫الصحراء الغربية‬‎)",
46686         "eh",
46687         "212",
46688         1
46689       ],
46690       [
46691         "Yemen (‫اليمن‬‎)",
46692         "ye",
46693         "967"
46694       ],
46695       [
46696         "Zambia",
46697         "zm",
46698         "260"
46699       ],
46700       [
46701         "Zimbabwe",
46702         "zw",
46703         "263"
46704       ],
46705       [
46706         "Åland Islands",
46707         "ax",
46708         "358",
46709         1
46710       ]
46711   ];
46712   
46713   return d;
46714 }/**
46715 *    This script refer to:
46716 *    Title: International Telephone Input
46717 *    Author: Jack O'Connor
46718 *    Code version:  v12.1.12
46719 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46720 **/
46721
46722 /**
46723  * @class Roo.bootstrap.form.PhoneInput
46724  * @extends Roo.bootstrap.form.TriggerField
46725  * An input with International dial-code selection
46726  
46727  * @cfg {String} defaultDialCode default '+852'
46728  * @cfg {Array} preferedCountries default []
46729   
46730  * @constructor
46731  * Create a new PhoneInput.
46732  * @param {Object} config Configuration options
46733  */
46734
46735 Roo.bootstrap.form.PhoneInput = function(config) {
46736     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46737 };
46738
46739 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46740         /**
46741         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46742         */
46743         listWidth: undefined,
46744         
46745         selectedClass: 'active',
46746         
46747         invalidClass : "has-warning",
46748         
46749         validClass: 'has-success',
46750         
46751         allowed: '0123456789',
46752         
46753         max_length: 15,
46754         
46755         /**
46756          * @cfg {String} defaultDialCode The default dial code when initializing the input
46757          */
46758         defaultDialCode: '+852',
46759         
46760         /**
46761          * @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
46762          */
46763         preferedCountries: false,
46764         
46765         getAutoCreate : function()
46766         {
46767             var data = Roo.bootstrap.form.PhoneInputData();
46768             var align = this.labelAlign || this.parentLabelAlign();
46769             var id = Roo.id();
46770             
46771             this.allCountries = [];
46772             this.dialCodeMapping = [];
46773             
46774             for (var i = 0; i < data.length; i++) {
46775               var c = data[i];
46776               this.allCountries[i] = {
46777                 name: c[0],
46778                 iso2: c[1],
46779                 dialCode: c[2],
46780                 priority: c[3] || 0,
46781                 areaCodes: c[4] || null
46782               };
46783               this.dialCodeMapping[c[2]] = {
46784                   name: c[0],
46785                   iso2: c[1],
46786                   priority: c[3] || 0,
46787                   areaCodes: c[4] || null
46788               };
46789             }
46790             
46791             var cfg = {
46792                 cls: 'form-group',
46793                 cn: []
46794             };
46795             
46796             var input =  {
46797                 tag: 'input',
46798                 id : id,
46799                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46800                 maxlength: this.max_length,
46801                 cls : 'form-control tel-input',
46802                 autocomplete: 'new-password'
46803             };
46804             
46805             var hiddenInput = {
46806                 tag: 'input',
46807                 type: 'hidden',
46808                 cls: 'hidden-tel-input'
46809             };
46810             
46811             if (this.name) {
46812                 hiddenInput.name = this.name;
46813             }
46814             
46815             if (this.disabled) {
46816                 input.disabled = true;
46817             }
46818             
46819             var flag_container = {
46820                 tag: 'div',
46821                 cls: 'flag-box',
46822                 cn: [
46823                     {
46824                         tag: 'div',
46825                         cls: 'flag'
46826                     },
46827                     {
46828                         tag: 'div',
46829                         cls: 'caret'
46830                     }
46831                 ]
46832             };
46833             
46834             var box = {
46835                 tag: 'div',
46836                 cls: this.hasFeedback ? 'has-feedback' : '',
46837                 cn: [
46838                     hiddenInput,
46839                     input,
46840                     {
46841                         tag: 'input',
46842                         cls: 'dial-code-holder',
46843                         disabled: true
46844                     }
46845                 ]
46846             };
46847             
46848             var container = {
46849                 cls: 'roo-select2-container input-group',
46850                 cn: [
46851                     flag_container,
46852                     box
46853                 ]
46854             };
46855             
46856             if (this.fieldLabel.length) {
46857                 var indicator = {
46858                     tag: 'i',
46859                     tooltip: 'This field is required'
46860                 };
46861                 
46862                 var label = {
46863                     tag: 'label',
46864                     'for':  id,
46865                     cls: 'control-label',
46866                     cn: []
46867                 };
46868                 
46869                 var label_text = {
46870                     tag: 'span',
46871                     html: this.fieldLabel
46872                 };
46873                 
46874                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46875                 label.cn = [
46876                     indicator,
46877                     label_text
46878                 ];
46879                 
46880                 if(this.indicatorpos == 'right') {
46881                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46882                     label.cn = [
46883                         label_text,
46884                         indicator
46885                     ];
46886                 }
46887                 
46888                 if(align == 'left') {
46889                     container = {
46890                         tag: 'div',
46891                         cn: [
46892                             container
46893                         ]
46894                     };
46895                     
46896                     if(this.labelWidth > 12){
46897                         label.style = "width: " + this.labelWidth + 'px';
46898                     }
46899                     if(this.labelWidth < 13 && this.labelmd == 0){
46900                         this.labelmd = this.labelWidth;
46901                     }
46902                     if(this.labellg > 0){
46903                         label.cls += ' col-lg-' + this.labellg;
46904                         input.cls += ' col-lg-' + (12 - this.labellg);
46905                     }
46906                     if(this.labelmd > 0){
46907                         label.cls += ' col-md-' + this.labelmd;
46908                         container.cls += ' col-md-' + (12 - this.labelmd);
46909                     }
46910                     if(this.labelsm > 0){
46911                         label.cls += ' col-sm-' + this.labelsm;
46912                         container.cls += ' col-sm-' + (12 - this.labelsm);
46913                     }
46914                     if(this.labelxs > 0){
46915                         label.cls += ' col-xs-' + this.labelxs;
46916                         container.cls += ' col-xs-' + (12 - this.labelxs);
46917                     }
46918                 }
46919             }
46920             
46921             cfg.cn = [
46922                 label,
46923                 container
46924             ];
46925             
46926             var settings = this;
46927             
46928             ['xs','sm','md','lg'].map(function(size){
46929                 if (settings[size]) {
46930                     cfg.cls += ' col-' + size + '-' + settings[size];
46931                 }
46932             });
46933             
46934             this.store = new Roo.data.Store({
46935                 proxy : new Roo.data.MemoryProxy({}),
46936                 reader : new Roo.data.JsonReader({
46937                     fields : [
46938                         {
46939                             'name' : 'name',
46940                             'type' : 'string'
46941                         },
46942                         {
46943                             'name' : 'iso2',
46944                             'type' : 'string'
46945                         },
46946                         {
46947                             'name' : 'dialCode',
46948                             'type' : 'string'
46949                         },
46950                         {
46951                             'name' : 'priority',
46952                             'type' : 'string'
46953                         },
46954                         {
46955                             'name' : 'areaCodes',
46956                             'type' : 'string'
46957                         }
46958                     ]
46959                 })
46960             });
46961             
46962             if(!this.preferedCountries) {
46963                 this.preferedCountries = [
46964                     'hk',
46965                     'gb',
46966                     'us'
46967                 ];
46968             }
46969             
46970             var p = this.preferedCountries.reverse();
46971             
46972             if(p) {
46973                 for (var i = 0; i < p.length; i++) {
46974                     for (var j = 0; j < this.allCountries.length; j++) {
46975                         if(this.allCountries[j].iso2 == p[i]) {
46976                             var t = this.allCountries[j];
46977                             this.allCountries.splice(j,1);
46978                             this.allCountries.unshift(t);
46979                         }
46980                     } 
46981                 }
46982             }
46983             
46984             this.store.proxy.data = {
46985                 success: true,
46986                 data: this.allCountries
46987             };
46988             
46989             return cfg;
46990         },
46991         
46992         initEvents : function()
46993         {
46994             this.createList();
46995             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46996             
46997             this.indicator = this.indicatorEl();
46998             this.flag = this.flagEl();
46999             this.dialCodeHolder = this.dialCodeHolderEl();
47000             
47001             this.trigger = this.el.select('div.flag-box',true).first();
47002             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47003             
47004             var _this = this;
47005             
47006             (function(){
47007                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47008                 _this.list.setWidth(lw);
47009             }).defer(100);
47010             
47011             this.list.on('mouseover', this.onViewOver, this);
47012             this.list.on('mousemove', this.onViewMove, this);
47013             this.inputEl().on("keyup", this.onKeyUp, this);
47014             this.inputEl().on("keypress", this.onKeyPress, this);
47015             
47016             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47017
47018             this.view = new Roo.View(this.list, this.tpl, {
47019                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47020             });
47021             
47022             this.view.on('click', this.onViewClick, this);
47023             this.setValue(this.defaultDialCode);
47024         },
47025         
47026         onTriggerClick : function(e)
47027         {
47028             Roo.log('trigger click');
47029             if(this.disabled){
47030                 return;
47031             }
47032             
47033             if(this.isExpanded()){
47034                 this.collapse();
47035                 this.hasFocus = false;
47036             }else {
47037                 this.store.load({});
47038                 this.hasFocus = true;
47039                 this.expand();
47040             }
47041         },
47042         
47043         isExpanded : function()
47044         {
47045             return this.list.isVisible();
47046         },
47047         
47048         collapse : function()
47049         {
47050             if(!this.isExpanded()){
47051                 return;
47052             }
47053             this.list.hide();
47054             Roo.get(document).un('mousedown', this.collapseIf, this);
47055             Roo.get(document).un('mousewheel', this.collapseIf, this);
47056             this.fireEvent('collapse', this);
47057             this.validate();
47058         },
47059         
47060         expand : function()
47061         {
47062             Roo.log('expand');
47063
47064             if(this.isExpanded() || !this.hasFocus){
47065                 return;
47066             }
47067             
47068             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47069             this.list.setWidth(lw);
47070             
47071             this.list.show();
47072             this.restrictHeight();
47073             
47074             Roo.get(document).on('mousedown', this.collapseIf, this);
47075             Roo.get(document).on('mousewheel', this.collapseIf, this);
47076             
47077             this.fireEvent('expand', this);
47078         },
47079         
47080         restrictHeight : function()
47081         {
47082             this.list.alignTo(this.inputEl(), this.listAlign);
47083             this.list.alignTo(this.inputEl(), this.listAlign);
47084         },
47085         
47086         onViewOver : function(e, t)
47087         {
47088             if(this.inKeyMode){
47089                 return;
47090             }
47091             var item = this.view.findItemFromChild(t);
47092             
47093             if(item){
47094                 var index = this.view.indexOf(item);
47095                 this.select(index, false);
47096             }
47097         },
47098
47099         // private
47100         onViewClick : function(view, doFocus, el, e)
47101         {
47102             var index = this.view.getSelectedIndexes()[0];
47103             
47104             var r = this.store.getAt(index);
47105             
47106             if(r){
47107                 this.onSelect(r, index);
47108             }
47109             if(doFocus !== false && !this.blockFocus){
47110                 this.inputEl().focus();
47111             }
47112         },
47113         
47114         onViewMove : function(e, t)
47115         {
47116             this.inKeyMode = false;
47117         },
47118         
47119         select : function(index, scrollIntoView)
47120         {
47121             this.selectedIndex = index;
47122             this.view.select(index);
47123             if(scrollIntoView !== false){
47124                 var el = this.view.getNode(index);
47125                 if(el){
47126                     this.list.scrollChildIntoView(el, false);
47127                 }
47128             }
47129         },
47130         
47131         createList : function()
47132         {
47133             this.list = Roo.get(document.body).createChild({
47134                 tag: 'ul',
47135                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47136                 style: 'display:none'
47137             });
47138             
47139             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47140         },
47141         
47142         collapseIf : function(e)
47143         {
47144             var in_combo  = e.within(this.el);
47145             var in_list =  e.within(this.list);
47146             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47147             
47148             if (in_combo || in_list || is_list) {
47149                 return;
47150             }
47151             this.collapse();
47152         },
47153         
47154         onSelect : function(record, index)
47155         {
47156             if(this.fireEvent('beforeselect', this, record, index) !== false){
47157                 
47158                 this.setFlagClass(record.data.iso2);
47159                 this.setDialCode(record.data.dialCode);
47160                 this.hasFocus = false;
47161                 this.collapse();
47162                 this.fireEvent('select', this, record, index);
47163             }
47164         },
47165         
47166         flagEl : function()
47167         {
47168             var flag = this.el.select('div.flag',true).first();
47169             if(!flag){
47170                 return false;
47171             }
47172             return flag;
47173         },
47174         
47175         dialCodeHolderEl : function()
47176         {
47177             var d = this.el.select('input.dial-code-holder',true).first();
47178             if(!d){
47179                 return false;
47180             }
47181             return d;
47182         },
47183         
47184         setDialCode : function(v)
47185         {
47186             this.dialCodeHolder.dom.value = '+'+v;
47187         },
47188         
47189         setFlagClass : function(n)
47190         {
47191             this.flag.dom.className = 'flag '+n;
47192         },
47193         
47194         getValue : function()
47195         {
47196             var v = this.inputEl().getValue();
47197             if(this.dialCodeHolder) {
47198                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47199             }
47200             return v;
47201         },
47202         
47203         setValue : function(v)
47204         {
47205             var d = this.getDialCode(v);
47206             
47207             //invalid dial code
47208             if(v.length == 0 || !d || d.length == 0) {
47209                 if(this.rendered){
47210                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
47211                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47212                 }
47213                 return;
47214             }
47215             
47216             //valid dial code
47217             this.setFlagClass(this.dialCodeMapping[d].iso2);
47218             this.setDialCode(d);
47219             this.inputEl().dom.value = v.replace('+'+d,'');
47220             this.hiddenEl().dom.value = this.getValue();
47221             
47222             this.validate();
47223         },
47224         
47225         getDialCode : function(v)
47226         {
47227             v = v ||  '';
47228             
47229             if (v.length == 0) {
47230                 return this.dialCodeHolder.dom.value;
47231             }
47232             
47233             var dialCode = "";
47234             if (v.charAt(0) != "+") {
47235                 return false;
47236             }
47237             var numericChars = "";
47238             for (var i = 1; i < v.length; i++) {
47239               var c = v.charAt(i);
47240               if (!isNaN(c)) {
47241                 numericChars += c;
47242                 if (this.dialCodeMapping[numericChars]) {
47243                   dialCode = v.substr(1, i);
47244                 }
47245                 if (numericChars.length == 4) {
47246                   break;
47247                 }
47248               }
47249             }
47250             return dialCode;
47251         },
47252         
47253         reset : function()
47254         {
47255             this.setValue(this.defaultDialCode);
47256             this.validate();
47257         },
47258         
47259         hiddenEl : function()
47260         {
47261             return this.el.select('input.hidden-tel-input',true).first();
47262         },
47263         
47264         // after setting val
47265         onKeyUp : function(e){
47266             this.setValue(this.getValue());
47267         },
47268         
47269         onKeyPress : function(e){
47270             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
47271                 e.stopEvent();
47272             }
47273         }
47274         
47275 });
47276 /**
47277  * @class Roo.bootstrap.form.MoneyField
47278  * @extends Roo.bootstrap.form.ComboBox
47279  * Bootstrap MoneyField class
47280  * 
47281  * @constructor
47282  * Create a new MoneyField.
47283  * @param {Object} config Configuration options
47284  */
47285
47286 Roo.bootstrap.form.MoneyField = function(config) {
47287     
47288     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
47289     
47290 };
47291
47292 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
47293     
47294     /**
47295      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
47296      */
47297     allowDecimals : true,
47298     /**
47299      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
47300      */
47301     decimalSeparator : ".",
47302     /**
47303      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
47304      */
47305     decimalPrecision : 0,
47306     /**
47307      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
47308      */
47309     allowNegative : true,
47310     /**
47311      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
47312      */
47313     allowZero: true,
47314     /**
47315      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
47316      */
47317     minValue : Number.NEGATIVE_INFINITY,
47318     /**
47319      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
47320      */
47321     maxValue : Number.MAX_VALUE,
47322     /**
47323      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
47324      */
47325     minText : "The minimum value for this field is {0}",
47326     /**
47327      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
47328      */
47329     maxText : "The maximum value for this field is {0}",
47330     /**
47331      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
47332      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
47333      */
47334     nanText : "{0} is not a valid number",
47335     /**
47336      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
47337      */
47338     castInt : true,
47339     /**
47340      * @cfg {String} defaults currency of the MoneyField
47341      * value should be in lkey
47342      */
47343     defaultCurrency : false,
47344     /**
47345      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
47346      */
47347     thousandsDelimiter : false,
47348     /**
47349      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
47350      */
47351     max_length: false,
47352     
47353     inputlg : 9,
47354     inputmd : 9,
47355     inputsm : 9,
47356     inputxs : 6,
47357      /**
47358      * @cfg {Roo.data.Store} store  Store to lookup currency??
47359      */
47360     store : false,
47361     
47362     getAutoCreate : function()
47363     {
47364         var align = this.labelAlign || this.parentLabelAlign();
47365         
47366         var id = Roo.id();
47367
47368         var cfg = {
47369             cls: 'form-group',
47370             cn: []
47371         };
47372
47373         var input =  {
47374             tag: 'input',
47375             id : id,
47376             cls : 'form-control roo-money-amount-input',
47377             autocomplete: 'new-password'
47378         };
47379         
47380         var hiddenInput = {
47381             tag: 'input',
47382             type: 'hidden',
47383             id: Roo.id(),
47384             cls: 'hidden-number-input'
47385         };
47386         
47387         if(this.max_length) {
47388             input.maxlength = this.max_length; 
47389         }
47390         
47391         if (this.name) {
47392             hiddenInput.name = this.name;
47393         }
47394
47395         if (this.disabled) {
47396             input.disabled = true;
47397         }
47398
47399         var clg = 12 - this.inputlg;
47400         var cmd = 12 - this.inputmd;
47401         var csm = 12 - this.inputsm;
47402         var cxs = 12 - this.inputxs;
47403         
47404         var container = {
47405             tag : 'div',
47406             cls : 'row roo-money-field',
47407             cn : [
47408                 {
47409                     tag : 'div',
47410                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
47411                     cn : [
47412                         {
47413                             tag : 'div',
47414                             cls: 'roo-select2-container input-group',
47415                             cn: [
47416                                 {
47417                                     tag : 'input',
47418                                     cls : 'form-control roo-money-currency-input',
47419                                     autocomplete: 'new-password',
47420                                     readOnly : 1,
47421                                     name : this.currencyName
47422                                 },
47423                                 {
47424                                     tag :'span',
47425                                     cls : 'input-group-addon',
47426                                     cn : [
47427                                         {
47428                                             tag: 'span',
47429                                             cls: 'caret'
47430                                         }
47431                                     ]
47432                                 }
47433                             ]
47434                         }
47435                     ]
47436                 },
47437                 {
47438                     tag : 'div',
47439                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
47440                     cn : [
47441                         {
47442                             tag: 'div',
47443                             cls: this.hasFeedback ? 'has-feedback' : '',
47444                             cn: [
47445                                 input
47446                             ]
47447                         }
47448                     ]
47449                 }
47450             ]
47451             
47452         };
47453         
47454         if (this.fieldLabel.length) {
47455             var indicator = {
47456                 tag: 'i',
47457                 tooltip: 'This field is required'
47458             };
47459
47460             var label = {
47461                 tag: 'label',
47462                 'for':  id,
47463                 cls: 'control-label',
47464                 cn: []
47465             };
47466
47467             var label_text = {
47468                 tag: 'span',
47469                 html: this.fieldLabel
47470             };
47471
47472             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47473             label.cn = [
47474                 indicator,
47475                 label_text
47476             ];
47477
47478             if(this.indicatorpos == 'right') {
47479                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47480                 label.cn = [
47481                     label_text,
47482                     indicator
47483                 ];
47484             }
47485
47486             if(align == 'left') {
47487                 container = {
47488                     tag: 'div',
47489                     cn: [
47490                         container
47491                     ]
47492                 };
47493
47494                 if(this.labelWidth > 12){
47495                     label.style = "width: " + this.labelWidth + 'px';
47496                 }
47497                 if(this.labelWidth < 13 && this.labelmd == 0){
47498                     this.labelmd = this.labelWidth;
47499                 }
47500                 if(this.labellg > 0){
47501                     label.cls += ' col-lg-' + this.labellg;
47502                     input.cls += ' col-lg-' + (12 - this.labellg);
47503                 }
47504                 if(this.labelmd > 0){
47505                     label.cls += ' col-md-' + this.labelmd;
47506                     container.cls += ' col-md-' + (12 - this.labelmd);
47507                 }
47508                 if(this.labelsm > 0){
47509                     label.cls += ' col-sm-' + this.labelsm;
47510                     container.cls += ' col-sm-' + (12 - this.labelsm);
47511                 }
47512                 if(this.labelxs > 0){
47513                     label.cls += ' col-xs-' + this.labelxs;
47514                     container.cls += ' col-xs-' + (12 - this.labelxs);
47515                 }
47516             }
47517         }
47518
47519         cfg.cn = [
47520             label,
47521             container,
47522             hiddenInput
47523         ];
47524         
47525         var settings = this;
47526
47527         ['xs','sm','md','lg'].map(function(size){
47528             if (settings[size]) {
47529                 cfg.cls += ' col-' + size + '-' + settings[size];
47530             }
47531         });
47532         
47533         return cfg;
47534     },
47535     
47536     initEvents : function()
47537     {
47538         this.indicator = this.indicatorEl();
47539         
47540         this.initCurrencyEvent();
47541         
47542         this.initNumberEvent();
47543     },
47544     
47545     initCurrencyEvent : function()
47546     {
47547         if (!this.store) {
47548             throw "can not find store for combo";
47549         }
47550         
47551         this.store = Roo.factory(this.store, Roo.data);
47552         this.store.parent = this;
47553         
47554         this.createList();
47555         
47556         this.triggerEl = this.el.select('.input-group-addon', true).first();
47557         
47558         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47559         
47560         var _this = this;
47561         
47562         (function(){
47563             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47564             _this.list.setWidth(lw);
47565         }).defer(100);
47566         
47567         this.list.on('mouseover', this.onViewOver, this);
47568         this.list.on('mousemove', this.onViewMove, this);
47569         this.list.on('scroll', this.onViewScroll, this);
47570         
47571         if(!this.tpl){
47572             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47573         }
47574         
47575         this.view = new Roo.View(this.list, this.tpl, {
47576             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47577         });
47578         
47579         this.view.on('click', this.onViewClick, this);
47580         
47581         this.store.on('beforeload', this.onBeforeLoad, this);
47582         this.store.on('load', this.onLoad, this);
47583         this.store.on('loadexception', this.onLoadException, this);
47584         
47585         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47586             "up" : function(e){
47587                 this.inKeyMode = true;
47588                 this.selectPrev();
47589             },
47590
47591             "down" : function(e){
47592                 if(!this.isExpanded()){
47593                     this.onTriggerClick();
47594                 }else{
47595                     this.inKeyMode = true;
47596                     this.selectNext();
47597                 }
47598             },
47599
47600             "enter" : function(e){
47601                 this.collapse();
47602                 
47603                 if(this.fireEvent("specialkey", this, e)){
47604                     this.onViewClick(false);
47605                 }
47606                 
47607                 return true;
47608             },
47609
47610             "esc" : function(e){
47611                 this.collapse();
47612             },
47613
47614             "tab" : function(e){
47615                 this.collapse();
47616                 
47617                 if(this.fireEvent("specialkey", this, e)){
47618                     this.onViewClick(false);
47619                 }
47620                 
47621                 return true;
47622             },
47623
47624             scope : this,
47625
47626             doRelay : function(foo, bar, hname){
47627                 if(hname == 'down' || this.scope.isExpanded()){
47628                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47629                 }
47630                 return true;
47631             },
47632
47633             forceKeyDown: true
47634         });
47635         
47636         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47637         
47638     },
47639     
47640     initNumberEvent : function(e)
47641     {
47642         this.inputEl().on("keydown" , this.fireKey,  this);
47643         this.inputEl().on("focus", this.onFocus,  this);
47644         this.inputEl().on("blur", this.onBlur,  this);
47645         
47646         this.inputEl().relayEvent('keyup', this);
47647         
47648         if(this.indicator){
47649             this.indicator.addClass('invisible');
47650         }
47651  
47652         this.originalValue = this.getValue();
47653         
47654         if(this.validationEvent == 'keyup'){
47655             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47656             this.inputEl().on('keyup', this.filterValidation, this);
47657         }
47658         else if(this.validationEvent !== false){
47659             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47660         }
47661         
47662         if(this.selectOnFocus){
47663             this.on("focus", this.preFocus, this);
47664             
47665         }
47666         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47667             this.inputEl().on("keypress", this.filterKeys, this);
47668         } else {
47669             this.inputEl().relayEvent('keypress', this);
47670         }
47671         
47672         var allowed = "0123456789";
47673         
47674         if(this.allowDecimals){
47675             allowed += this.decimalSeparator;
47676         }
47677         
47678         if(this.allowNegative){
47679             allowed += "-";
47680         }
47681         
47682         if(this.thousandsDelimiter) {
47683             allowed += ",";
47684         }
47685         
47686         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47687         
47688         var keyPress = function(e){
47689             
47690             var k = e.getKey();
47691             
47692             var c = e.getCharCode();
47693             
47694             if(
47695                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47696                     allowed.indexOf(String.fromCharCode(c)) === -1
47697             ){
47698                 e.stopEvent();
47699                 return;
47700             }
47701             
47702             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47703                 return;
47704             }
47705             
47706             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47707                 e.stopEvent();
47708             }
47709         };
47710         
47711         this.inputEl().on("keypress", keyPress, this);
47712         
47713     },
47714     
47715     onTriggerClick : function(e)
47716     {   
47717         if(this.disabled){
47718             return;
47719         }
47720         
47721         this.page = 0;
47722         this.loadNext = false;
47723         
47724         if(this.isExpanded()){
47725             this.collapse();
47726             return;
47727         }
47728         
47729         this.hasFocus = true;
47730         
47731         if(this.triggerAction == 'all') {
47732             this.doQuery(this.allQuery, true);
47733             return;
47734         }
47735         
47736         this.doQuery(this.getRawValue());
47737     },
47738     
47739     getCurrency : function()
47740     {   
47741         var v = this.currencyEl().getValue();
47742         
47743         return v;
47744     },
47745     
47746     restrictHeight : function()
47747     {
47748         this.list.alignTo(this.currencyEl(), this.listAlign);
47749         this.list.alignTo(this.currencyEl(), this.listAlign);
47750     },
47751     
47752     onViewClick : function(view, doFocus, el, e)
47753     {
47754         var index = this.view.getSelectedIndexes()[0];
47755         
47756         var r = this.store.getAt(index);
47757         
47758         if(r){
47759             this.onSelect(r, index);
47760         }
47761     },
47762     
47763     onSelect : function(record, index){
47764         
47765         if(this.fireEvent('beforeselect', this, record, index) !== false){
47766         
47767             this.setFromCurrencyData(index > -1 ? record.data : false);
47768             
47769             this.collapse();
47770             
47771             this.fireEvent('select', this, record, index);
47772         }
47773     },
47774     
47775     setFromCurrencyData : function(o)
47776     {
47777         var currency = '';
47778         
47779         this.lastCurrency = o;
47780         
47781         if (this.currencyField) {
47782             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47783         } else {
47784             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47785         }
47786         
47787         this.lastSelectionText = currency;
47788         
47789         //setting default currency
47790         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47791             this.setCurrency(this.defaultCurrency);
47792             return;
47793         }
47794         
47795         this.setCurrency(currency);
47796     },
47797     
47798     setFromData : function(o)
47799     {
47800         var c = {};
47801         
47802         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47803         
47804         this.setFromCurrencyData(c);
47805         
47806         var value = '';
47807         
47808         if (this.name) {
47809             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47810         } else {
47811             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47812         }
47813         
47814         this.setValue(value);
47815         
47816     },
47817     
47818     setCurrency : function(v)
47819     {   
47820         this.currencyValue = v;
47821         
47822         if(this.rendered){
47823             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47824             this.validate();
47825         }
47826     },
47827     
47828     setValue : function(v)
47829     {
47830         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47831         
47832         this.value = v;
47833         
47834         if(this.rendered){
47835             
47836             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47837             
47838             this.inputEl().dom.value = (v == '') ? '' :
47839                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47840             
47841             if(!this.allowZero && v === '0') {
47842                 this.hiddenEl().dom.value = '';
47843                 this.inputEl().dom.value = '';
47844             }
47845             
47846             this.validate();
47847         }
47848     },
47849     
47850     getRawValue : function()
47851     {
47852         var v = this.inputEl().getValue();
47853         
47854         return v;
47855     },
47856     
47857     getValue : function()
47858     {
47859         return this.fixPrecision(this.parseValue(this.getRawValue()));
47860     },
47861     
47862     parseValue : function(value)
47863     {
47864         if(this.thousandsDelimiter) {
47865             value += "";
47866             r = new RegExp(",", "g");
47867             value = value.replace(r, "");
47868         }
47869         
47870         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47871         return isNaN(value) ? '' : value;
47872         
47873     },
47874     
47875     fixPrecision : function(value)
47876     {
47877         if(this.thousandsDelimiter) {
47878             value += "";
47879             r = new RegExp(",", "g");
47880             value = value.replace(r, "");
47881         }
47882         
47883         var nan = isNaN(value);
47884         
47885         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47886             return nan ? '' : value;
47887         }
47888         return parseFloat(value).toFixed(this.decimalPrecision);
47889     },
47890     
47891     decimalPrecisionFcn : function(v)
47892     {
47893         return Math.floor(v);
47894     },
47895     
47896     validateValue : function(value)
47897     {
47898         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47899             return false;
47900         }
47901         
47902         var num = this.parseValue(value);
47903         
47904         if(isNaN(num)){
47905             this.markInvalid(String.format(this.nanText, value));
47906             return false;
47907         }
47908         
47909         if(num < this.minValue){
47910             this.markInvalid(String.format(this.minText, this.minValue));
47911             return false;
47912         }
47913         
47914         if(num > this.maxValue){
47915             this.markInvalid(String.format(this.maxText, this.maxValue));
47916             return false;
47917         }
47918         
47919         return true;
47920     },
47921     
47922     validate : function()
47923     {
47924         if(this.disabled || this.allowBlank){
47925             this.markValid();
47926             return true;
47927         }
47928         
47929         var currency = this.getCurrency();
47930         
47931         if(this.validateValue(this.getRawValue()) && currency.length){
47932             this.markValid();
47933             return true;
47934         }
47935         
47936         this.markInvalid();
47937         return false;
47938     },
47939     
47940     getName: function()
47941     {
47942         return this.name;
47943     },
47944     
47945     beforeBlur : function()
47946     {
47947         if(!this.castInt){
47948             return;
47949         }
47950         
47951         var v = this.parseValue(this.getRawValue());
47952         
47953         if(v || v == 0){
47954             this.setValue(v);
47955         }
47956     },
47957     
47958     onBlur : function()
47959     {
47960         this.beforeBlur();
47961         
47962         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47963             //this.el.removeClass(this.focusClass);
47964         }
47965         
47966         this.hasFocus = false;
47967         
47968         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47969             this.validate();
47970         }
47971         
47972         var v = this.getValue();
47973         
47974         if(String(v) !== String(this.startValue)){
47975             this.fireEvent('change', this, v, this.startValue);
47976         }
47977         
47978         this.fireEvent("blur", this);
47979     },
47980     
47981     inputEl : function()
47982     {
47983         return this.el.select('.roo-money-amount-input', true).first();
47984     },
47985     
47986     currencyEl : function()
47987     {
47988         return this.el.select('.roo-money-currency-input', true).first();
47989     },
47990     
47991     hiddenEl : function()
47992     {
47993         return this.el.select('input.hidden-number-input',true).first();
47994     }
47995     
47996 });/**
47997  * @class Roo.bootstrap.BezierSignature
47998  * @extends Roo.bootstrap.Component
47999  * Bootstrap BezierSignature class
48000  * This script refer to:
48001  *    Title: Signature Pad
48002  *    Author: szimek
48003  *    Availability: https://github.com/szimek/signature_pad
48004  *
48005  * @constructor
48006  * Create a new BezierSignature
48007  * @param {Object} config The config object
48008  */
48009
48010 Roo.bootstrap.BezierSignature = function(config){
48011     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48012     this.addEvents({
48013         "resize" : true
48014     });
48015 };
48016
48017 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48018 {
48019      
48020     curve_data: [],
48021     
48022     is_empty: true,
48023     
48024     mouse_btn_down: true,
48025     
48026     /**
48027      * @cfg {int} canvas height
48028      */
48029     canvas_height: '200px',
48030     
48031     /**
48032      * @cfg {float|function} Radius of a single dot.
48033      */ 
48034     dot_size: false,
48035     
48036     /**
48037      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48038      */
48039     min_width: 0.5,
48040     
48041     /**
48042      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48043      */
48044     max_width: 2.5,
48045     
48046     /**
48047      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48048      */
48049     throttle: 16,
48050     
48051     /**
48052      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48053      */
48054     min_distance: 5,
48055     
48056     /**
48057      * @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.
48058      */
48059     bg_color: 'rgba(0, 0, 0, 0)',
48060     
48061     /**
48062      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48063      */
48064     dot_color: 'black',
48065     
48066     /**
48067      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48068      */ 
48069     velocity_filter_weight: 0.7,
48070     
48071     /**
48072      * @cfg {function} Callback when stroke begin. 
48073      */
48074     onBegin: false,
48075     
48076     /**
48077      * @cfg {function} Callback when stroke end.
48078      */
48079     onEnd: false,
48080     
48081     getAutoCreate : function()
48082     {
48083         var cls = 'roo-signature column';
48084         
48085         if(this.cls){
48086             cls += ' ' + this.cls;
48087         }
48088         
48089         var col_sizes = [
48090             'lg',
48091             'md',
48092             'sm',
48093             'xs'
48094         ];
48095         
48096         for(var i = 0; i < col_sizes.length; i++) {
48097             if(this[col_sizes[i]]) {
48098                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48099             }
48100         }
48101         
48102         var cfg = {
48103             tag: 'div',
48104             cls: cls,
48105             cn: [
48106                 {
48107                     tag: 'div',
48108                     cls: 'roo-signature-body',
48109                     cn: [
48110                         {
48111                             tag: 'canvas',
48112                             cls: 'roo-signature-body-canvas',
48113                             height: this.canvas_height,
48114                             width: this.canvas_width
48115                         }
48116                     ]
48117                 },
48118                 {
48119                     tag: 'input',
48120                     type: 'file',
48121                     style: 'display: none'
48122                 }
48123             ]
48124         };
48125         
48126         return cfg;
48127     },
48128     
48129     initEvents: function() 
48130     {
48131         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48132         
48133         var canvas = this.canvasEl();
48134         
48135         // mouse && touch event swapping...
48136         canvas.dom.style.touchAction = 'none';
48137         canvas.dom.style.msTouchAction = 'none';
48138         
48139         this.mouse_btn_down = false;
48140         canvas.on('mousedown', this._handleMouseDown, this);
48141         canvas.on('mousemove', this._handleMouseMove, this);
48142         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48143         
48144         if (window.PointerEvent) {
48145             canvas.on('pointerdown', this._handleMouseDown, this);
48146             canvas.on('pointermove', this._handleMouseMove, this);
48147             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48148         }
48149         
48150         if ('ontouchstart' in window) {
48151             canvas.on('touchstart', this._handleTouchStart, this);
48152             canvas.on('touchmove', this._handleTouchMove, this);
48153             canvas.on('touchend', this._handleTouchEnd, this);
48154         }
48155         
48156         Roo.EventManager.onWindowResize(this.resize, this, true);
48157         
48158         // file input event
48159         this.fileEl().on('change', this.uploadImage, this);
48160         
48161         this.clear();
48162         
48163         this.resize();
48164     },
48165     
48166     resize: function(){
48167         
48168         var canvas = this.canvasEl().dom;
48169         var ctx = this.canvasElCtx();
48170         var img_data = false;
48171         
48172         if(canvas.width > 0) {
48173             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48174         }
48175         // setting canvas width will clean img data
48176         canvas.width = 0;
48177         
48178         var style = window.getComputedStyle ? 
48179             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48180             
48181         var padding_left = parseInt(style.paddingLeft) || 0;
48182         var padding_right = parseInt(style.paddingRight) || 0;
48183         
48184         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48185         
48186         if(img_data) {
48187             ctx.putImageData(img_data, 0, 0);
48188         }
48189     },
48190     
48191     _handleMouseDown: function(e)
48192     {
48193         if (e.browserEvent.which === 1) {
48194             this.mouse_btn_down = true;
48195             this.strokeBegin(e);
48196         }
48197     },
48198     
48199     _handleMouseMove: function (e)
48200     {
48201         if (this.mouse_btn_down) {
48202             this.strokeMoveUpdate(e);
48203         }
48204     },
48205     
48206     _handleMouseUp: function (e)
48207     {
48208         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
48209             this.mouse_btn_down = false;
48210             this.strokeEnd(e);
48211         }
48212     },
48213     
48214     _handleTouchStart: function (e) {
48215         
48216         e.preventDefault();
48217         if (e.browserEvent.targetTouches.length === 1) {
48218             // var touch = e.browserEvent.changedTouches[0];
48219             // this.strokeBegin(touch);
48220             
48221              this.strokeBegin(e); // assume e catching the correct xy...
48222         }
48223     },
48224     
48225     _handleTouchMove: function (e) {
48226         e.preventDefault();
48227         // var touch = event.targetTouches[0];
48228         // _this._strokeMoveUpdate(touch);
48229         this.strokeMoveUpdate(e);
48230     },
48231     
48232     _handleTouchEnd: function (e) {
48233         var wasCanvasTouched = e.target === this.canvasEl().dom;
48234         if (wasCanvasTouched) {
48235             e.preventDefault();
48236             // var touch = event.changedTouches[0];
48237             // _this._strokeEnd(touch);
48238             this.strokeEnd(e);
48239         }
48240     },
48241     
48242     reset: function () {
48243         this._lastPoints = [];
48244         this._lastVelocity = 0;
48245         this._lastWidth = (this.min_width + this.max_width) / 2;
48246         this.canvasElCtx().fillStyle = this.dot_color;
48247     },
48248     
48249     strokeMoveUpdate: function(e)
48250     {
48251         this.strokeUpdate(e);
48252         
48253         if (this.throttle) {
48254             this.throttleStroke(this.strokeUpdate, this.throttle);
48255         }
48256         else {
48257             this.strokeUpdate(e);
48258         }
48259     },
48260     
48261     strokeBegin: function(e)
48262     {
48263         var newPointGroup = {
48264             color: this.dot_color,
48265             points: []
48266         };
48267         
48268         if (typeof this.onBegin === 'function') {
48269             this.onBegin(e);
48270         }
48271         
48272         this.curve_data.push(newPointGroup);
48273         this.reset();
48274         this.strokeUpdate(e);
48275     },
48276     
48277     strokeUpdate: function(e)
48278     {
48279         var rect = this.canvasEl().dom.getBoundingClientRect();
48280         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
48281         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
48282         var lastPoints = lastPointGroup.points;
48283         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
48284         var isLastPointTooClose = lastPoint
48285             ? point.distanceTo(lastPoint) <= this.min_distance
48286             : false;
48287         var color = lastPointGroup.color;
48288         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
48289             var curve = this.addPoint(point);
48290             if (!lastPoint) {
48291                 this.drawDot({color: color, point: point});
48292             }
48293             else if (curve) {
48294                 this.drawCurve({color: color, curve: curve});
48295             }
48296             lastPoints.push({
48297                 time: point.time,
48298                 x: point.x,
48299                 y: point.y
48300             });
48301         }
48302     },
48303     
48304     strokeEnd: function(e)
48305     {
48306         this.strokeUpdate(e);
48307         if (typeof this.onEnd === 'function') {
48308             this.onEnd(e);
48309         }
48310     },
48311     
48312     addPoint:  function (point) {
48313         var _lastPoints = this._lastPoints;
48314         _lastPoints.push(point);
48315         if (_lastPoints.length > 2) {
48316             if (_lastPoints.length === 3) {
48317                 _lastPoints.unshift(_lastPoints[0]);
48318             }
48319             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
48320             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
48321             _lastPoints.shift();
48322             return curve;
48323         }
48324         return null;
48325     },
48326     
48327     calculateCurveWidths: function (startPoint, endPoint) {
48328         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
48329             (1 - this.velocity_filter_weight) * this._lastVelocity;
48330
48331         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
48332         var widths = {
48333             end: newWidth,
48334             start: this._lastWidth
48335         };
48336         
48337         this._lastVelocity = velocity;
48338         this._lastWidth = newWidth;
48339         return widths;
48340     },
48341     
48342     drawDot: function (_a) {
48343         var color = _a.color, point = _a.point;
48344         var ctx = this.canvasElCtx();
48345         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
48346         ctx.beginPath();
48347         this.drawCurveSegment(point.x, point.y, width);
48348         ctx.closePath();
48349         ctx.fillStyle = color;
48350         ctx.fill();
48351     },
48352     
48353     drawCurve: function (_a) {
48354         var color = _a.color, curve = _a.curve;
48355         var ctx = this.canvasElCtx();
48356         var widthDelta = curve.endWidth - curve.startWidth;
48357         var drawSteps = Math.floor(curve.length()) * 2;
48358         ctx.beginPath();
48359         ctx.fillStyle = color;
48360         for (var i = 0; i < drawSteps; i += 1) {
48361         var t = i / drawSteps;
48362         var tt = t * t;
48363         var ttt = tt * t;
48364         var u = 1 - t;
48365         var uu = u * u;
48366         var uuu = uu * u;
48367         var x = uuu * curve.startPoint.x;
48368         x += 3 * uu * t * curve.control1.x;
48369         x += 3 * u * tt * curve.control2.x;
48370         x += ttt * curve.endPoint.x;
48371         var y = uuu * curve.startPoint.y;
48372         y += 3 * uu * t * curve.control1.y;
48373         y += 3 * u * tt * curve.control2.y;
48374         y += ttt * curve.endPoint.y;
48375         var width = curve.startWidth + ttt * widthDelta;
48376         this.drawCurveSegment(x, y, width);
48377         }
48378         ctx.closePath();
48379         ctx.fill();
48380     },
48381     
48382     drawCurveSegment: function (x, y, width) {
48383         var ctx = this.canvasElCtx();
48384         ctx.moveTo(x, y);
48385         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
48386         this.is_empty = false;
48387     },
48388     
48389     clear: function()
48390     {
48391         var ctx = this.canvasElCtx();
48392         var canvas = this.canvasEl().dom;
48393         ctx.fillStyle = this.bg_color;
48394         ctx.clearRect(0, 0, canvas.width, canvas.height);
48395         ctx.fillRect(0, 0, canvas.width, canvas.height);
48396         this.curve_data = [];
48397         this.reset();
48398         this.is_empty = true;
48399     },
48400     
48401     fileEl: function()
48402     {
48403         return  this.el.select('input',true).first();
48404     },
48405     
48406     canvasEl: function()
48407     {
48408         return this.el.select('canvas',true).first();
48409     },
48410     
48411     canvasElCtx: function()
48412     {
48413         return this.el.select('canvas',true).first().dom.getContext('2d');
48414     },
48415     
48416     getImage: function(type)
48417     {
48418         if(this.is_empty) {
48419             return false;
48420         }
48421         
48422         // encryption ?
48423         return this.canvasEl().dom.toDataURL('image/'+type, 1);
48424     },
48425     
48426     drawFromImage: function(img_src)
48427     {
48428         var img = new Image();
48429         
48430         img.onload = function(){
48431             this.canvasElCtx().drawImage(img, 0, 0);
48432         }.bind(this);
48433         
48434         img.src = img_src;
48435         
48436         this.is_empty = false;
48437     },
48438     
48439     selectImage: function()
48440     {
48441         this.fileEl().dom.click();
48442     },
48443     
48444     uploadImage: function(e)
48445     {
48446         var reader = new FileReader();
48447         
48448         reader.onload = function(e){
48449             var img = new Image();
48450             img.onload = function(){
48451                 this.reset();
48452                 this.canvasElCtx().drawImage(img, 0, 0);
48453             }.bind(this);
48454             img.src = e.target.result;
48455         }.bind(this);
48456         
48457         reader.readAsDataURL(e.target.files[0]);
48458     },
48459     
48460     // Bezier Point Constructor
48461     Point: (function () {
48462         function Point(x, y, time) {
48463             this.x = x;
48464             this.y = y;
48465             this.time = time || Date.now();
48466         }
48467         Point.prototype.distanceTo = function (start) {
48468             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
48469         };
48470         Point.prototype.equals = function (other) {
48471             return this.x === other.x && this.y === other.y && this.time === other.time;
48472         };
48473         Point.prototype.velocityFrom = function (start) {
48474             return this.time !== start.time
48475             ? this.distanceTo(start) / (this.time - start.time)
48476             : 0;
48477         };
48478         return Point;
48479     }()),
48480     
48481     
48482     // Bezier Constructor
48483     Bezier: (function () {
48484         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
48485             this.startPoint = startPoint;
48486             this.control2 = control2;
48487             this.control1 = control1;
48488             this.endPoint = endPoint;
48489             this.startWidth = startWidth;
48490             this.endWidth = endWidth;
48491         }
48492         Bezier.fromPoints = function (points, widths, scope) {
48493             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
48494             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
48495             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
48496         };
48497         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
48498             var dx1 = s1.x - s2.x;
48499             var dy1 = s1.y - s2.y;
48500             var dx2 = s2.x - s3.x;
48501             var dy2 = s2.y - s3.y;
48502             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
48503             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
48504             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
48505             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
48506             var dxm = m1.x - m2.x;
48507             var dym = m1.y - m2.y;
48508             var k = l2 / (l1 + l2);
48509             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
48510             var tx = s2.x - cm.x;
48511             var ty = s2.y - cm.y;
48512             return {
48513                 c1: new scope.Point(m1.x + tx, m1.y + ty),
48514                 c2: new scope.Point(m2.x + tx, m2.y + ty)
48515             };
48516         };
48517         Bezier.prototype.length = function () {
48518             var steps = 10;
48519             var length = 0;
48520             var px;
48521             var py;
48522             for (var i = 0; i <= steps; i += 1) {
48523                 var t = i / steps;
48524                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
48525                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
48526                 if (i > 0) {
48527                     var xdiff = cx - px;
48528                     var ydiff = cy - py;
48529                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48530                 }
48531                 px = cx;
48532                 py = cy;
48533             }
48534             return length;
48535         };
48536         Bezier.prototype.point = function (t, start, c1, c2, end) {
48537             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48538             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48539             + (3.0 * c2 * (1.0 - t) * t * t)
48540             + (end * t * t * t);
48541         };
48542         return Bezier;
48543     }()),
48544     
48545     throttleStroke: function(fn, wait) {
48546       if (wait === void 0) { wait = 250; }
48547       var previous = 0;
48548       var timeout = null;
48549       var result;
48550       var storedContext;
48551       var storedArgs;
48552       var later = function () {
48553           previous = Date.now();
48554           timeout = null;
48555           result = fn.apply(storedContext, storedArgs);
48556           if (!timeout) {
48557               storedContext = null;
48558               storedArgs = [];
48559           }
48560       };
48561       return function wrapper() {
48562           var args = [];
48563           for (var _i = 0; _i < arguments.length; _i++) {
48564               args[_i] = arguments[_i];
48565           }
48566           var now = Date.now();
48567           var remaining = wait - (now - previous);
48568           storedContext = this;
48569           storedArgs = args;
48570           if (remaining <= 0 || remaining > wait) {
48571               if (timeout) {
48572                   clearTimeout(timeout);
48573                   timeout = null;
48574               }
48575               previous = now;
48576               result = fn.apply(storedContext, storedArgs);
48577               if (!timeout) {
48578                   storedContext = null;
48579                   storedArgs = [];
48580               }
48581           }
48582           else if (!timeout) {
48583               timeout = window.setTimeout(later, remaining);
48584           }
48585           return result;
48586       };
48587   }
48588   
48589 });
48590
48591  
48592
48593  // old names for form elements
48594 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48595 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48596 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48597 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48598 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48599 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48600 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48601 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48602 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48603 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48604 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48605 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48606 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48607 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48608 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48609 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48610 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48611 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48612 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48613 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48614 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48615 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48616 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48617 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48618 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48619 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48620
48621 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48622 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48623
48624 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48625 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48626
48627 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48628 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48629 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48630 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48631