Roo/bootstrap/form/TimeField.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     footerRow : false,
9346     headerShow : true,
9347     enableColumnResize: true,
9348     disableAutoSize: false,
9349   
9350     rowSelection : false,
9351     cellSelection : false,
9352     layout : false,
9353
9354     minColumnWidth : 50,
9355     
9356     // Roo.Element - the tbody
9357     bodyEl: false,  // <tbody> Roo.Element - thead element    
9358     headEl: false,  // <thead> Roo.Element - thead element
9359     resizeProxy : false, // proxy element for dragging?
9360
9361
9362     
9363     container: false, // used by gridpanel...
9364     
9365     lazyLoad : false,
9366     
9367     CSS : Roo.util.CSS,
9368     
9369     auto_hide_footer : false,
9370     
9371     view: false, // actually points to this..
9372     
9373     getAutoCreate : function()
9374     {
9375         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9376         
9377         cfg = {
9378             tag: 'table',
9379             cls : 'table', 
9380             cn : []
9381         };
9382         // this get's auto added by panel.Grid
9383         if (this.scrollBody) {
9384             cfg.cls += ' table-body-fixed';
9385         }    
9386         if (this.striped) {
9387             cfg.cls += ' table-striped';
9388         }
9389         
9390         if (this.hover) {
9391             cfg.cls += ' table-hover';
9392         }
9393         if (this.bordered) {
9394             cfg.cls += ' table-bordered';
9395         }
9396         if (this.condensed) {
9397             cfg.cls += ' table-condensed';
9398         }
9399         
9400         if (this.responsive) {
9401             cfg.cls += ' table-responsive';
9402         }
9403         
9404         if (this.cls) {
9405             cfg.cls+=  ' ' +this.cls;
9406         }
9407         
9408         
9409         
9410         if (this.layout) {
9411             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412         }
9413         
9414         if(this.store || this.cm){
9415             if(this.headerShow){
9416                 cfg.cn.push(this.renderHeader());
9417             }
9418             
9419             cfg.cn.push(this.renderBody());
9420             
9421             if(this.footerShow || this.footerRow){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             // where does this come from?
9426             //cfg.cls+=  ' TableGrid';
9427         }
9428         
9429         return { cn : [ cfg ] };
9430     },
9431     
9432     initEvents : function()
9433     {   
9434         if(!this.store || !this.cm){
9435             return;
9436         }
9437         if (this.selModel) {
9438             this.selModel.initEvents();
9439         }
9440         
9441         
9442         //Roo.log('initEvents with ds!!!!');
9443         
9444         this.bodyEl = this.el.select('tbody', true).first();
9445         this.headEl = this.el.select('thead', true).first();
9446         this.mainFoot = this.el.select('tfoot', true).first();
9447         
9448         
9449         
9450         
9451         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9452             e.on('click', this.sort, this);
9453         }, this);
9454         
9455         
9456         // why is this done????? = it breaks dialogs??
9457         //this.parent().el.setStyle('position', 'relative');
9458         
9459         
9460         if (this.footer) {
9461             this.footer.parentId = this.id;
9462             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9463             
9464             if(this.lazyLoad){
9465                 this.el.select('tfoot tr td').first().addClass('hide');
9466             }
9467         } 
9468         
9469         if(this.loadMask) {
9470             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9471         }
9472         
9473         this.store.on('load', this.onLoad, this);
9474         this.store.on('beforeload', this.onBeforeLoad, this);
9475         this.store.on('update', this.onUpdate, this);
9476         this.store.on('add', this.onAdd, this);
9477         this.store.on("clear", this.clear, this);
9478         
9479         this.el.on("contextmenu", this.onContextMenu, this);
9480         
9481         
9482         this.cm.on("headerchange", this.onHeaderChange, this);
9483         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9484
9485  //?? does bodyEl get replaced on render?
9486         this.bodyEl.on("click", this.onClick, this);
9487         this.bodyEl.on("dblclick", this.onDblClick, this);        
9488         this.bodyEl.on('scroll', this.onBodyScroll, this);
9489
9490         // guessing mainbody will work - this relays usually caught by selmodel at present.
9491         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9492   
9493   
9494         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9495         
9496   
9497         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9498             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9499         }
9500         
9501         this.initCSS();
9502     },
9503     // Compatibility with grid - we implement all the view features at present.
9504     getView : function()
9505     {
9506         return this;
9507     },
9508     
9509     initCSS : function()
9510     {
9511         if(this.disableAutoSize) {
9512             return;
9513         }
9514         
9515         var cm = this.cm, styles = [];
9516         this.CSS.removeStyleSheet(this.id + '-cssrules');
9517         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9518         // we can honour xs/sm/md/xl  as widths...
9519         // we first have to decide what widht we are currently at...
9520         var sz = Roo.getGridSize();
9521         
9522         var total = 0;
9523         var last = -1;
9524         var cols = []; // visable cols.
9525         var total_abs = 0;
9526         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9527             var w = cm.getColumnWidth(i, false);
9528             if(cm.isHidden(i)){
9529                 cols.push( { rel : false, abs : 0 });
9530                 continue;
9531             }
9532             if (w !== false) {
9533                 cols.push( { rel : false, abs : w });
9534                 total_abs += w;
9535                 last = i; // not really..
9536                 continue;
9537             }
9538             var w = cm.getColumnWidth(i, sz);
9539             if (w > 0) {
9540                 last = i
9541             }
9542             total += w;
9543             cols.push( { rel : w, abs : false });
9544         }
9545         
9546         var avail = this.bodyEl.dom.clientWidth - total_abs;
9547         
9548         var unitWidth = Math.floor(avail / total);
9549         var rem = avail - (unitWidth * total);
9550         
9551         var hidden, width, pos = 0 , splithide , left;
9552         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9553             
9554             hidden = 'display:none;';
9555             left = '';
9556             width  = 'width:0px;';
9557             splithide = '';
9558             if(!cm.isHidden(i)){
9559                 hidden = '';
9560                 
9561                 
9562                 // we can honour xs/sm/md/xl ?
9563                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9564                 if (w===0) {
9565                     hidden = 'display:none;';
9566                 }
9567                 // width should return a small number...
9568                 if (i == last) {
9569                     w+=rem; // add the remaining with..
9570                 }
9571                 pos += w;
9572                 left = "left:" + (pos -4) + "px;";
9573                 width = "width:" + w+ "px;";
9574                 
9575             }
9576             if (this.responsive) {
9577                 width = '';
9578                 left = '';
9579                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9580                 splithide = 'display: none;';
9581             }
9582             
9583             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9584             if (this.headEl) {
9585                 if (i == last) {
9586                     splithide = 'display:none;';
9587                 }
9588                 
9589                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9590                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9591                             // this is the popover version..
9592                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9593                 );
9594             }
9595             
9596         }
9597         //Roo.log(styles.join(''));
9598         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9599         
9600     },
9601     
9602     
9603     
9604     onContextMenu : function(e, t)
9605     {
9606         this.processEvent("contextmenu", e);
9607     },
9608     
9609     processEvent : function(name, e)
9610     {
9611         if (name != 'touchstart' ) {
9612             this.fireEvent(name, e);    
9613         }
9614         
9615         var t = e.getTarget();
9616         
9617         var cell = Roo.get(t);
9618         
9619         if(!cell){
9620             return;
9621         }
9622         
9623         if(cell.findParent('tfoot', false, true)){
9624             return;
9625         }
9626         
9627         if(cell.findParent('thead', false, true)){
9628             
9629             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9630                 cell = Roo.get(t).findParent('th', false, true);
9631                 if (!cell) {
9632                     Roo.log("failed to find th in thead?");
9633                     Roo.log(e.getTarget());
9634                     return;
9635                 }
9636             }
9637             
9638             var cellIndex = cell.dom.cellIndex;
9639             
9640             var ename = name == 'touchstart' ? 'click' : name;
9641             this.fireEvent("header" + ename, this, cellIndex, e);
9642             
9643             return;
9644         }
9645         
9646         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9647             cell = Roo.get(t).findParent('td', false, true);
9648             if (!cell) {
9649                 Roo.log("failed to find th in tbody?");
9650                 Roo.log(e.getTarget());
9651                 return;
9652             }
9653         }
9654         
9655         var row = cell.findParent('tr', false, true);
9656         var cellIndex = cell.dom.cellIndex;
9657         var rowIndex = row.dom.rowIndex - 1;
9658         
9659         if(row !== false){
9660             
9661             this.fireEvent("row" + name, this, rowIndex, e);
9662             
9663             if(cell !== false){
9664             
9665                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9666             }
9667         }
9668         
9669     },
9670     
9671     onMouseover : function(e, el)
9672     {
9673         var cell = Roo.get(el);
9674         
9675         if(!cell){
9676             return;
9677         }
9678         
9679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9680             cell = cell.findParent('td', false, true);
9681         }
9682         
9683         var row = cell.findParent('tr', false, true);
9684         var cellIndex = cell.dom.cellIndex;
9685         var rowIndex = row.dom.rowIndex - 1; // start from 0
9686         
9687         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9688         
9689     },
9690     
9691     onMouseout : function(e, el)
9692     {
9693         var cell = Roo.get(el);
9694         
9695         if(!cell){
9696             return;
9697         }
9698         
9699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9700             cell = cell.findParent('td', false, true);
9701         }
9702         
9703         var row = cell.findParent('tr', false, true);
9704         var cellIndex = cell.dom.cellIndex;
9705         var rowIndex = row.dom.rowIndex - 1; // start from 0
9706         
9707         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9708         
9709     },
9710     
9711     onClick : function(e, el)
9712     {
9713         var cell = Roo.get(el);
9714         
9715         if(!cell || (!this.cellSelection && !this.rowSelection)){
9716             return;
9717         }
9718         
9719         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9720             cell = cell.findParent('td', false, true);
9721         }
9722         
9723         if(!cell || typeof(cell) == 'undefined'){
9724             return;
9725         }
9726         
9727         var row = cell.findParent('tr', false, true);
9728         
9729         if(!row || typeof(row) == 'undefined'){
9730             return;
9731         }
9732         
9733         var cellIndex = cell.dom.cellIndex;
9734         var rowIndex = this.getRowIndex(row);
9735         
9736         // why??? - should these not be based on SelectionModel?
9737         //if(this.cellSelection){
9738             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9739         //}
9740         
9741         //if(this.rowSelection){
9742             this.fireEvent('rowclick', this, row, rowIndex, e);
9743         //}
9744          
9745     },
9746         
9747     onDblClick : function(e,el)
9748     {
9749         var cell = Roo.get(el);
9750         
9751         if(!cell || (!this.cellSelection && !this.rowSelection)){
9752             return;
9753         }
9754         
9755         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9756             cell = cell.findParent('td', false, true);
9757         }
9758         
9759         if(!cell || typeof(cell) == 'undefined'){
9760             return;
9761         }
9762         
9763         var row = cell.findParent('tr', false, true);
9764         
9765         if(!row || typeof(row) == 'undefined'){
9766             return;
9767         }
9768         
9769         var cellIndex = cell.dom.cellIndex;
9770         var rowIndex = this.getRowIndex(row);
9771         
9772         if(this.cellSelection){
9773             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9774         }
9775         
9776         if(this.rowSelection){
9777             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9778         }
9779     },
9780     findRowIndex : function(el)
9781     {
9782         var cell = Roo.get(el);
9783         if(!cell) {
9784             return false;
9785         }
9786         var row = cell.findParent('tr', false, true);
9787         
9788         if(!row || typeof(row) == 'undefined'){
9789             return false;
9790         }
9791         return this.getRowIndex(row);
9792     },
9793     sort : function(e,el)
9794     {
9795         var col = Roo.get(el);
9796         
9797         if(!col.hasClass('sortable')){
9798             return;
9799         }
9800         
9801         var sort = col.attr('sort');
9802         var dir = 'ASC';
9803         
9804         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9805             dir = 'DESC';
9806         }
9807         
9808         this.store.sortInfo = {field : sort, direction : dir};
9809         
9810         if (this.footer) {
9811             Roo.log("calling footer first");
9812             this.footer.onClick('first');
9813         } else {
9814         
9815             this.store.load({ params : { start : 0 } });
9816         }
9817     },
9818     
9819     renderHeader : function()
9820     {
9821         var header = {
9822             tag: 'thead',
9823             cn : []
9824         };
9825         
9826         var cm = this.cm;
9827         this.totalWidth = 0;
9828         
9829         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9830             
9831             var config = cm.config[i];
9832             
9833             var c = {
9834                 tag: 'th',
9835                 cls : 'x-hcol-' + i,
9836                 style : '',
9837                 
9838                 html: cm.getColumnHeader(i)
9839             };
9840             
9841             var tooltip = cm.getColumnTooltip(i);
9842             if (tooltip) {
9843                 c.tooltip = tooltip;
9844             }
9845             
9846             
9847             var hh = '';
9848             
9849             if(typeof(config.sortable) != 'undefined' && config.sortable){
9850                 c.cls += ' sortable';
9851                 c.html = '<i class="fa"></i>' + c.html;
9852             }
9853             
9854             // could use BS4 hidden-..-down 
9855             
9856             if(typeof(config.lgHeader) != 'undefined'){
9857                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9858             }
9859             
9860             if(typeof(config.mdHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.smHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.xsHeader) != 'undefined'){
9869                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9870             }
9871             
9872             if(hh.length){
9873                 c.html = hh;
9874             }
9875             
9876             if(typeof(config.tooltip) != 'undefined'){
9877                 c.tooltip = config.tooltip;
9878             }
9879             
9880             if(typeof(config.colspan) != 'undefined'){
9881                 c.colspan = config.colspan;
9882             }
9883             
9884             // hidden is handled by CSS now
9885             
9886             if(typeof(config.dataIndex) != 'undefined'){
9887                 c.sort = config.dataIndex;
9888             }
9889             
9890            
9891             
9892             if(typeof(config.align) != 'undefined' && config.align.length){
9893                 c.style += ' text-align:' + config.align + ';';
9894             }
9895             
9896             /* width is done in CSS
9897              *if(typeof(config.width) != 'undefined'){
9898                 c.style += ' width:' + config.width + 'px;';
9899                 this.totalWidth += config.width;
9900             } else {
9901                 this.totalWidth += 100; // assume minimum of 100 per column?
9902             }
9903             */
9904             
9905             if(typeof(config.cls) != 'undefined'){
9906                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9907             }
9908             // this is the bit that doesnt reall work at all...
9909             
9910             if (this.responsive) {
9911                  
9912             
9913                 ['xs','sm','md','lg'].map(function(size){
9914                     
9915                     if(typeof(config[size]) == 'undefined'){
9916                         return;
9917                     }
9918                      
9919                     if (!config[size]) { // 0 = hidden
9920                         // BS 4 '0' is treated as hide that column and below.
9921                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9922                         return;
9923                     }
9924                     
9925                     c.cls += ' col-' + size + '-' + config[size] + (
9926                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9927                     );
9928                     
9929                     
9930                 });
9931             }
9932             // at the end?
9933             
9934             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9935             
9936             
9937             
9938             
9939             header.cn.push(c)
9940         }
9941         
9942         return header;
9943     },
9944     
9945     renderBody : function()
9946     {
9947         var body = {
9948             tag: 'tbody',
9949             cn : [
9950                 {
9951                     tag: 'tr',
9952                     cn : [
9953                         {
9954                             tag : 'td',
9955                             colspan :  this.cm.getColumnCount()
9956                         }
9957                     ]
9958                 }
9959             ]
9960         };
9961         
9962         return body;
9963     },
9964     
9965     renderFooter : function()
9966     {
9967         var footer = {
9968             tag: 'tfoot',
9969             cn : [
9970                 {
9971                     tag: 'tr',
9972                     cn : [
9973                         {
9974                             tag : 'td',
9975                             colspan :  this.cm.getColumnCount()
9976                         }
9977                     ]
9978                 }
9979             ]
9980         };
9981         
9982         return footer;
9983     },
9984     
9985     onLoad : function()
9986     {
9987 //        Roo.log('ds onload');
9988         this.clear();
9989         
9990         var _this = this;
9991         var cm = this.cm;
9992         var ds = this.store;
9993         
9994         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9995             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9996             if (_this.store.sortInfo) {
9997                     
9998                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9999                     e.select('i', true).addClass(['fa-arrow-up']);
10000                 }
10001                 
10002                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10003                     e.select('i', true).addClass(['fa-arrow-down']);
10004                 }
10005             }
10006         });
10007         
10008         var tbody =  this.bodyEl;
10009               
10010         if(ds.getCount() > 0){
10011             ds.data.each(function(d,rowIndex){
10012                 var row =  this.renderRow(cm, ds, rowIndex);
10013                 
10014                 tbody.createChild(row);
10015                 
10016                 var _this = this;
10017                 
10018                 if(row.cellObjects.length){
10019                     Roo.each(row.cellObjects, function(r){
10020                         _this.renderCellObject(r);
10021                     })
10022                 }
10023                 
10024             }, this);
10025         } else if (this.empty_results.length) {
10026             this.el.mask(this.empty_results, 'no-spinner');
10027         }
10028         
10029         var tfoot = this.el.select('tfoot', true).first();
10030         
10031         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10032             
10033             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10034             
10035             var total = this.ds.getTotalCount();
10036             
10037             if(this.footer.pageSize < total){
10038                 this.mainFoot.show();
10039             }
10040         }
10041
10042         if(!this.footerShow && this.footerRow) {
10043
10044             var tr = {
10045                 tag : 'tr',
10046                 cn : []
10047             };
10048
10049             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10050                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10051                 var td = {
10052                     tag: 'td',
10053                     cls : ' x-fcol-' + i,
10054                     html: footer
10055                 };
10056
10057                 tr.cn.push(td);
10058                 
10059             }
10060             
10061             tfoot.dom.innerHTML = '';
10062
10063             tfoot.createChild(tr);
10064         }
10065         
10066         Roo.each(this.el.select('tbody td', true).elements, function(e){
10067             e.on('mouseover', _this.onMouseover, _this);
10068         });
10069         
10070         Roo.each(this.el.select('tbody td', true).elements, function(e){
10071             e.on('mouseout', _this.onMouseout, _this);
10072         });
10073         this.fireEvent('rowsrendered', this);
10074         
10075         this.autoSize();
10076         
10077         this.initCSS(); /// resize cols
10078
10079         
10080     },
10081     
10082     
10083     onUpdate : function(ds,record)
10084     {
10085         this.refreshRow(record);
10086         this.autoSize();
10087     },
10088     
10089     onRemove : function(ds, record, index, isUpdate){
10090         if(isUpdate !== true){
10091             this.fireEvent("beforerowremoved", this, index, record);
10092         }
10093         var bt = this.bodyEl.dom;
10094         
10095         var rows = this.el.select('tbody > tr', true).elements;
10096         
10097         if(typeof(rows[index]) != 'undefined'){
10098             bt.removeChild(rows[index].dom);
10099         }
10100         
10101 //        if(bt.rows[index]){
10102 //            bt.removeChild(bt.rows[index]);
10103 //        }
10104         
10105         if(isUpdate !== true){
10106             //this.stripeRows(index);
10107             //this.syncRowHeights(index, index);
10108             //this.layout();
10109             this.fireEvent("rowremoved", this, index, record);
10110         }
10111     },
10112     
10113     onAdd : function(ds, records, rowIndex)
10114     {
10115         //Roo.log('on Add called');
10116         // - note this does not handle multiple adding very well..
10117         var bt = this.bodyEl.dom;
10118         for (var i =0 ; i < records.length;i++) {
10119             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10120             //Roo.log(records[i]);
10121             //Roo.log(this.store.getAt(rowIndex+i));
10122             this.insertRow(this.store, rowIndex + i, false);
10123             return;
10124         }
10125         
10126     },
10127     
10128     
10129     refreshRow : function(record){
10130         var ds = this.store, index;
10131         if(typeof record == 'number'){
10132             index = record;
10133             record = ds.getAt(index);
10134         }else{
10135             index = ds.indexOf(record);
10136             if (index < 0) {
10137                 return; // should not happen - but seems to 
10138             }
10139         }
10140         this.insertRow(ds, index, true);
10141         this.autoSize();
10142         this.onRemove(ds, record, index+1, true);
10143         this.autoSize();
10144         //this.syncRowHeights(index, index);
10145         //this.layout();
10146         this.fireEvent("rowupdated", this, index, record);
10147     },
10148     // private - called by RowSelection
10149     onRowSelect : function(rowIndex){
10150         var row = this.getRowDom(rowIndex);
10151         row.addClass(['bg-info','info']);
10152     },
10153     // private - called by RowSelection
10154     onRowDeselect : function(rowIndex)
10155     {
10156         if (rowIndex < 0) {
10157             return;
10158         }
10159         var row = this.getRowDom(rowIndex);
10160         row.removeClass(['bg-info','info']);
10161     },
10162       /**
10163      * Focuses the specified row.
10164      * @param {Number} row The row index
10165      */
10166     focusRow : function(row)
10167     {
10168         //Roo.log('GridView.focusRow');
10169         var x = this.bodyEl.dom.scrollLeft;
10170         this.focusCell(row, 0, false);
10171         this.bodyEl.dom.scrollLeft = x;
10172
10173     },
10174      /**
10175      * Focuses the specified cell.
10176      * @param {Number} row The row index
10177      * @param {Number} col The column index
10178      * @param {Boolean} hscroll false to disable horizontal scrolling
10179      */
10180     focusCell : function(row, col, hscroll)
10181     {
10182         //Roo.log('GridView.focusCell');
10183         var el = this.ensureVisible(row, col, hscroll);
10184         // not sure what focusEL achives = it's a <a> pos relative 
10185         //this.focusEl.alignTo(el, "tl-tl");
10186         //if(Roo.isGecko){
10187         //    this.focusEl.focus();
10188         //}else{
10189         //    this.focusEl.focus.defer(1, this.focusEl);
10190         //}
10191     },
10192     
10193      /**
10194      * Scrolls the specified cell into view
10195      * @param {Number} row The row index
10196      * @param {Number} col The column index
10197      * @param {Boolean} hscroll false to disable horizontal scrolling
10198      */
10199     ensureVisible : function(row, col, hscroll)
10200     {
10201         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10202         //return null; //disable for testing.
10203         if(typeof row != "number"){
10204             row = row.rowIndex;
10205         }
10206         if(row < 0 && row >= this.ds.getCount()){
10207             return  null;
10208         }
10209         col = (col !== undefined ? col : 0);
10210         var cm = this.cm;
10211         while(cm.isHidden(col)){
10212             col++;
10213         }
10214
10215         var el = this.getCellDom(row, col);
10216         if(!el){
10217             return null;
10218         }
10219         var c = this.bodyEl.dom;
10220
10221         var ctop = parseInt(el.offsetTop, 10);
10222         var cleft = parseInt(el.offsetLeft, 10);
10223         var cbot = ctop + el.offsetHeight;
10224         var cright = cleft + el.offsetWidth;
10225
10226         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10227         var ch = 0; //?? header is not withing the area?
10228         var stop = parseInt(c.scrollTop, 10);
10229         var sleft = parseInt(c.scrollLeft, 10);
10230         var sbot = stop + ch;
10231         var sright = sleft + c.clientWidth;
10232         /*
10233         Roo.log('GridView.ensureVisible:' +
10234                 ' ctop:' + ctop +
10235                 ' c.clientHeight:' + c.clientHeight +
10236                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10237                 ' stop:' + stop +
10238                 ' cbot:' + cbot +
10239                 ' sbot:' + sbot +
10240                 ' ch:' + ch  
10241                 );
10242         */
10243         if(ctop < stop){
10244             c.scrollTop = ctop;
10245             //Roo.log("set scrolltop to ctop DISABLE?");
10246         }else if(cbot > sbot){
10247             //Roo.log("set scrolltop to cbot-ch");
10248             c.scrollTop = cbot-ch;
10249         }
10250
10251         if(hscroll !== false){
10252             if(cleft < sleft){
10253                 c.scrollLeft = cleft;
10254             }else if(cright > sright){
10255                 c.scrollLeft = cright-c.clientWidth;
10256             }
10257         }
10258
10259         return el;
10260     },
10261     
10262     
10263     insertRow : function(dm, rowIndex, isUpdate){
10264         
10265         if(!isUpdate){
10266             this.fireEvent("beforerowsinserted", this, rowIndex);
10267         }
10268             //var s = this.getScrollState();
10269         var row = this.renderRow(this.cm, this.store, rowIndex);
10270         // insert before rowIndex..
10271         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10272         
10273         var _this = this;
10274                 
10275         if(row.cellObjects.length){
10276             Roo.each(row.cellObjects, function(r){
10277                 _this.renderCellObject(r);
10278             })
10279         }
10280             
10281         if(!isUpdate){
10282             this.fireEvent("rowsinserted", this, rowIndex);
10283             //this.syncRowHeights(firstRow, lastRow);
10284             //this.stripeRows(firstRow);
10285             //this.layout();
10286         }
10287         
10288     },
10289     
10290     
10291     getRowDom : function(rowIndex)
10292     {
10293         var rows = this.el.select('tbody > tr', true).elements;
10294         
10295         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10296         
10297     },
10298     getCellDom : function(rowIndex, colIndex)
10299     {
10300         var row = this.getRowDom(rowIndex);
10301         if (row === false) {
10302             return false;
10303         }
10304         var cols = row.select('td', true).elements;
10305         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10306         
10307     },
10308     
10309     // returns the object tree for a tr..
10310   
10311     
10312     renderRow : function(cm, ds, rowIndex) 
10313     {
10314         var d = ds.getAt(rowIndex);
10315         
10316         var row = {
10317             tag : 'tr',
10318             cls : 'x-row-' + rowIndex,
10319             cn : []
10320         };
10321             
10322         var cellObjects = [];
10323         
10324         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10325             var config = cm.config[i];
10326             
10327             var renderer = cm.getRenderer(i);
10328             var value = '';
10329             var id = false;
10330             
10331             if(typeof(renderer) !== 'undefined'){
10332                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10333             }
10334             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10335             // and are rendered into the cells after the row is rendered - using the id for the element.
10336             
10337             if(typeof(value) === 'object'){
10338                 id = Roo.id();
10339                 cellObjects.push({
10340                     container : id,
10341                     cfg : value 
10342                 })
10343             }
10344             
10345             var rowcfg = {
10346                 record: d,
10347                 rowIndex : rowIndex,
10348                 colIndex : i,
10349                 rowClass : ''
10350             };
10351
10352             this.fireEvent('rowclass', this, rowcfg);
10353             
10354             var td = {
10355                 tag: 'td',
10356                 // this might end up displaying HTML?
10357                 // this is too messy... - better to only do it on columsn you know are going to be too long
10358                 //tooltip : (typeof(value) === 'object') ? '' : value,
10359                 cls : rowcfg.rowClass + ' x-col-' + i,
10360                 style: '',
10361                 html: (typeof(value) === 'object') ? '' : value
10362             };
10363             
10364             if (id) {
10365                 td.id = id;
10366             }
10367             
10368             if(typeof(config.colspan) != 'undefined'){
10369                 td.colspan = config.colspan;
10370             }
10371             
10372             
10373             
10374             if(typeof(config.align) != 'undefined' && config.align.length){
10375                 td.style += ' text-align:' + config.align + ';';
10376             }
10377             if(typeof(config.valign) != 'undefined' && config.valign.length){
10378                 td.style += ' vertical-align:' + config.valign + ';';
10379             }
10380             /*
10381             if(typeof(config.width) != 'undefined'){
10382                 td.style += ' width:' +  config.width + 'px;';
10383             }
10384             */
10385             
10386             if(typeof(config.cursor) != 'undefined'){
10387                 td.style += ' cursor:' +  config.cursor + ';';
10388             }
10389             
10390             if(typeof(config.cls) != 'undefined'){
10391                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10392             }
10393             if (this.responsive) {
10394                 ['xs','sm','md','lg'].map(function(size){
10395                     
10396                     if(typeof(config[size]) == 'undefined'){
10397                         return;
10398                     }
10399                     
10400                     
10401                       
10402                     if (!config[size]) { // 0 = hidden
10403                         // BS 4 '0' is treated as hide that column and below.
10404                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10405                         return;
10406                     }
10407                     
10408                     td.cls += ' col-' + size + '-' + config[size] + (
10409                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10410                     );
10411                      
10412     
10413                 });
10414             }
10415             row.cn.push(td);
10416            
10417         }
10418         
10419         row.cellObjects = cellObjects;
10420         
10421         return row;
10422           
10423     },
10424     
10425     
10426     
10427     onBeforeLoad : function()
10428     {
10429         this.el.unmask(); // if needed.
10430     },
10431      /**
10432      * Remove all rows
10433      */
10434     clear : function()
10435     {
10436         this.el.select('tbody', true).first().dom.innerHTML = '';
10437     },
10438     /**
10439      * Show or hide a row.
10440      * @param {Number} rowIndex to show or hide
10441      * @param {Boolean} state hide
10442      */
10443     setRowVisibility : function(rowIndex, state)
10444     {
10445         var bt = this.bodyEl.dom;
10446         
10447         var rows = this.el.select('tbody > tr', true).elements;
10448         
10449         if(typeof(rows[rowIndex]) == 'undefined'){
10450             return;
10451         }
10452         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10453         
10454     },
10455     
10456     
10457     getSelectionModel : function(){
10458         if(!this.selModel){
10459             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10460         }
10461         return this.selModel;
10462     },
10463     /*
10464      * Render the Roo.bootstrap object from renderder
10465      */
10466     renderCellObject : function(r)
10467     {
10468         var _this = this;
10469         
10470         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10471         
10472         var t = r.cfg.render(r.container);
10473         
10474         if(r.cfg.cn){
10475             Roo.each(r.cfg.cn, function(c){
10476                 var child = {
10477                     container: t.getChildContainer(),
10478                     cfg: c
10479                 };
10480                 _this.renderCellObject(child);
10481             })
10482         }
10483     },
10484     /**
10485      * get the Row Index from a dom element.
10486      * @param {Roo.Element} row The row to look for
10487      * @returns {Number} the row
10488      */
10489     getRowIndex : function(row)
10490     {
10491         var rowIndex = -1;
10492         
10493         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10494             if(el != row){
10495                 return;
10496             }
10497             
10498             rowIndex = index;
10499         });
10500         
10501         return rowIndex;
10502     },
10503     /**
10504      * get the header TH element for columnIndex
10505      * @param {Number} columnIndex
10506      * @returns {Roo.Element}
10507      */
10508     getHeaderIndex: function(colIndex)
10509     {
10510         var cols = this.headEl.select('th', true).elements;
10511         return cols[colIndex]; 
10512     },
10513     /**
10514      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10515      * @param {domElement} cell to look for
10516      * @returns {Number} the column
10517      */
10518     getCellIndex : function(cell)
10519     {
10520         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10521         if(id){
10522             return parseInt(id[1], 10);
10523         }
10524         return 0;
10525     },
10526      /**
10527      * Returns the grid's underlying element = used by panel.Grid
10528      * @return {Element} The element
10529      */
10530     getGridEl : function(){
10531         return this.el;
10532     },
10533      /**
10534      * Forces a resize - used by panel.Grid
10535      * @return {Element} The element
10536      */
10537     autoSize : function()
10538     {
10539         if(this.disableAutoSize) {
10540             return;
10541         }
10542         //var ctr = Roo.get(this.container.dom.parentElement);
10543         var ctr = Roo.get(this.el.dom);
10544         
10545         var thd = this.getGridEl().select('thead',true).first();
10546         var tbd = this.getGridEl().select('tbody', true).first();
10547         var tfd = this.getGridEl().select('tfoot', true).first();
10548         
10549         var cw = ctr.getWidth();
10550         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10551         
10552         if (tbd) {
10553             
10554             tbd.setWidth(ctr.getWidth());
10555             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10556             // this needs fixing for various usage - currently only hydra job advers I think..
10557             //tdb.setHeight(
10558             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10559             //); 
10560             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10561             cw -= barsize;
10562         }
10563         cw = Math.max(cw, this.totalWidth);
10564         this.getGridEl().select('tbody tr',true).setWidth(cw);
10565         this.initCSS();
10566         
10567         // resize 'expandable coloumn?
10568         
10569         return; // we doe not have a view in this design..
10570         
10571     },
10572     onBodyScroll: function()
10573     {
10574         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10575         if(this.headEl){
10576             this.headEl.setStyle({
10577                 'position' : 'relative',
10578                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10579             });
10580         }
10581         
10582         if(this.lazyLoad){
10583             
10584             var scrollHeight = this.bodyEl.dom.scrollHeight;
10585             
10586             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10587             
10588             var height = this.bodyEl.getHeight();
10589             
10590             if(scrollHeight - height == scrollTop) {
10591                 
10592                 var total = this.ds.getTotalCount();
10593                 
10594                 if(this.footer.cursor + this.footer.pageSize < total){
10595                     
10596                     this.footer.ds.load({
10597                         params : {
10598                             start : this.footer.cursor + this.footer.pageSize,
10599                             limit : this.footer.pageSize
10600                         },
10601                         add : true
10602                     });
10603                 }
10604             }
10605             
10606         }
10607     },
10608     onColumnSplitterMoved : function(i, diff)
10609     {
10610         this.userResized = true;
10611         
10612         var cm = this.colModel;
10613         
10614         var w = this.getHeaderIndex(i).getWidth() + diff;
10615         
10616         
10617         cm.setColumnWidth(i, w, true);
10618         this.initCSS();
10619         //var cid = cm.getColumnId(i); << not used in this version?
10620        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10621         
10622         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10623         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10624         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10625 */
10626         //this.updateSplitters();
10627         //this.layout(); << ??
10628         this.fireEvent("columnresize", i, w);
10629     },
10630     onHeaderChange : function()
10631     {
10632         var header = this.renderHeader();
10633         var table = this.el.select('table', true).first();
10634         
10635         this.headEl.remove();
10636         this.headEl = table.createChild(header, this.bodyEl, false);
10637         
10638         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10639             e.on('click', this.sort, this);
10640         }, this);
10641         
10642         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10643             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10644         }
10645         
10646     },
10647     
10648     onHiddenChange : function(colModel, colIndex, hidden)
10649     {
10650         /*
10651         this.cm.setHidden()
10652         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10653         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10654         
10655         this.CSS.updateRule(thSelector, "display", "");
10656         this.CSS.updateRule(tdSelector, "display", "");
10657         
10658         if(hidden){
10659             this.CSS.updateRule(thSelector, "display", "none");
10660             this.CSS.updateRule(tdSelector, "display", "none");
10661         }
10662         */
10663         // onload calls initCSS()
10664         this.onHeaderChange();
10665         this.onLoad();
10666     },
10667     
10668     setColumnWidth: function(col_index, width)
10669     {
10670         // width = "md-2 xs-2..."
10671         if(!this.colModel.config[col_index]) {
10672             return;
10673         }
10674         
10675         var w = width.split(" ");
10676         
10677         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10678         
10679         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10680         
10681         
10682         for(var j = 0; j < w.length; j++) {
10683             
10684             if(!w[j]) {
10685                 continue;
10686             }
10687             
10688             var size_cls = w[j].split("-");
10689             
10690             if(!Number.isInteger(size_cls[1] * 1)) {
10691                 continue;
10692             }
10693             
10694             if(!this.colModel.config[col_index][size_cls[0]]) {
10695                 continue;
10696             }
10697             
10698             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10699                 continue;
10700             }
10701             
10702             h_row[0].classList.replace(
10703                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10704                 "col-"+size_cls[0]+"-"+size_cls[1]
10705             );
10706             
10707             for(var i = 0; i < rows.length; i++) {
10708                 
10709                 var size_cls = w[j].split("-");
10710                 
10711                 if(!Number.isInteger(size_cls[1] * 1)) {
10712                     continue;
10713                 }
10714                 
10715                 if(!this.colModel.config[col_index][size_cls[0]]) {
10716                     continue;
10717                 }
10718                 
10719                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10720                     continue;
10721                 }
10722                 
10723                 rows[i].classList.replace(
10724                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10725                     "col-"+size_cls[0]+"-"+size_cls[1]
10726                 );
10727             }
10728             
10729             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10730         }
10731     }
10732 });
10733
10734 // currently only used to find the split on drag.. 
10735 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10736
10737 /**
10738  * @depricated
10739 */
10740 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10741 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10742 /*
10743  * - LGPL
10744  *
10745  * table cell
10746  * 
10747  */
10748
10749 /**
10750  * @class Roo.bootstrap.TableCell
10751  * @extends Roo.bootstrap.Component
10752  * @children Roo.bootstrap.Component
10753  * @parent Roo.bootstrap.TableRow
10754  * Bootstrap TableCell class
10755  * 
10756  * @cfg {String} html cell contain text
10757  * @cfg {String} cls cell class
10758  * @cfg {String} tag cell tag (td|th) default td
10759  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10760  * @cfg {String} align Aligns the content in a cell
10761  * @cfg {String} axis Categorizes cells
10762  * @cfg {String} bgcolor Specifies the background color of a cell
10763  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10764  * @cfg {Number} colspan Specifies the number of columns a cell should span
10765  * @cfg {String} headers Specifies one or more header cells a cell is related to
10766  * @cfg {Number} height Sets the height of a cell
10767  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10768  * @cfg {Number} rowspan Sets the number of rows a cell should span
10769  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10770  * @cfg {String} valign Vertical aligns the content in a cell
10771  * @cfg {Number} width Specifies the width of a cell
10772  * 
10773  * @constructor
10774  * Create a new TableCell
10775  * @param {Object} config The config object
10776  */
10777
10778 Roo.bootstrap.TableCell = function(config){
10779     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10780 };
10781
10782 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10783     
10784     html: false,
10785     cls: false,
10786     tag: false,
10787     abbr: false,
10788     align: false,
10789     axis: false,
10790     bgcolor: false,
10791     charoff: false,
10792     colspan: false,
10793     headers: false,
10794     height: false,
10795     nowrap: false,
10796     rowspan: false,
10797     scope: false,
10798     valign: false,
10799     width: false,
10800     
10801     
10802     getAutoCreate : function(){
10803         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10804         
10805         cfg = {
10806             tag: 'td'
10807         };
10808         
10809         if(this.tag){
10810             cfg.tag = this.tag;
10811         }
10812         
10813         if (this.html) {
10814             cfg.html=this.html
10815         }
10816         if (this.cls) {
10817             cfg.cls=this.cls
10818         }
10819         if (this.abbr) {
10820             cfg.abbr=this.abbr
10821         }
10822         if (this.align) {
10823             cfg.align=this.align
10824         }
10825         if (this.axis) {
10826             cfg.axis=this.axis
10827         }
10828         if (this.bgcolor) {
10829             cfg.bgcolor=this.bgcolor
10830         }
10831         if (this.charoff) {
10832             cfg.charoff=this.charoff
10833         }
10834         if (this.colspan) {
10835             cfg.colspan=this.colspan
10836         }
10837         if (this.headers) {
10838             cfg.headers=this.headers
10839         }
10840         if (this.height) {
10841             cfg.height=this.height
10842         }
10843         if (this.nowrap) {
10844             cfg.nowrap=this.nowrap
10845         }
10846         if (this.rowspan) {
10847             cfg.rowspan=this.rowspan
10848         }
10849         if (this.scope) {
10850             cfg.scope=this.scope
10851         }
10852         if (this.valign) {
10853             cfg.valign=this.valign
10854         }
10855         if (this.width) {
10856             cfg.width=this.width
10857         }
10858         
10859         
10860         return cfg;
10861     }
10862    
10863 });
10864
10865  
10866
10867  /*
10868  * - LGPL
10869  *
10870  * table row
10871  * 
10872  */
10873
10874 /**
10875  * @class Roo.bootstrap.TableRow
10876  * @extends Roo.bootstrap.Component
10877  * @children Roo.bootstrap.TableCell
10878  * @parent Roo.bootstrap.TableBody
10879  * Bootstrap TableRow class
10880  * @cfg {String} cls row class
10881  * @cfg {String} align Aligns the content in a table row
10882  * @cfg {String} bgcolor Specifies a background color for a table row
10883  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10884  * @cfg {String} valign Vertical aligns the content in a table row
10885  * 
10886  * @constructor
10887  * Create a new TableRow
10888  * @param {Object} config The config object
10889  */
10890
10891 Roo.bootstrap.TableRow = function(config){
10892     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10893 };
10894
10895 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10896     
10897     cls: false,
10898     align: false,
10899     bgcolor: false,
10900     charoff: false,
10901     valign: false,
10902     
10903     getAutoCreate : function(){
10904         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10905         
10906         cfg = {
10907             tag: 'tr'
10908         };
10909             
10910         if(this.cls){
10911             cfg.cls = this.cls;
10912         }
10913         if(this.align){
10914             cfg.align = this.align;
10915         }
10916         if(this.bgcolor){
10917             cfg.bgcolor = this.bgcolor;
10918         }
10919         if(this.charoff){
10920             cfg.charoff = this.charoff;
10921         }
10922         if(this.valign){
10923             cfg.valign = this.valign;
10924         }
10925         
10926         return cfg;
10927     }
10928    
10929 });
10930
10931  
10932
10933  /*
10934  * - LGPL
10935  *
10936  * table body
10937  * 
10938  */
10939
10940 /**
10941  * @class Roo.bootstrap.TableBody
10942  * @extends Roo.bootstrap.Component
10943  * @children Roo.bootstrap.TableRow
10944  * @parent Roo.bootstrap.Table
10945  * Bootstrap TableBody class
10946  * @cfg {String} cls element class
10947  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10948  * @cfg {String} align Aligns the content inside the element
10949  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10950  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10951  * 
10952  * @constructor
10953  * Create a new TableBody
10954  * @param {Object} config The config object
10955  */
10956
10957 Roo.bootstrap.TableBody = function(config){
10958     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10959 };
10960
10961 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10962     
10963     cls: false,
10964     tag: false,
10965     align: false,
10966     charoff: false,
10967     valign: false,
10968     
10969     getAutoCreate : function(){
10970         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10971         
10972         cfg = {
10973             tag: 'tbody'
10974         };
10975             
10976         if (this.cls) {
10977             cfg.cls=this.cls
10978         }
10979         if(this.tag){
10980             cfg.tag = this.tag;
10981         }
10982         
10983         if(this.align){
10984             cfg.align = this.align;
10985         }
10986         if(this.charoff){
10987             cfg.charoff = this.charoff;
10988         }
10989         if(this.valign){
10990             cfg.valign = this.valign;
10991         }
10992         
10993         return cfg;
10994     }
10995     
10996     
10997 //    initEvents : function()
10998 //    {
10999 //        
11000 //        if(!this.store){
11001 //            return;
11002 //        }
11003 //        
11004 //        this.store = Roo.factory(this.store, Roo.data);
11005 //        this.store.on('load', this.onLoad, this);
11006 //        
11007 //        this.store.load();
11008 //        
11009 //    },
11010 //    
11011 //    onLoad: function () 
11012 //    {   
11013 //        this.fireEvent('load', this);
11014 //    }
11015 //    
11016 //   
11017 });
11018
11019  
11020
11021  /*
11022  * Based on:
11023  * Ext JS Library 1.1.1
11024  * Copyright(c) 2006-2007, Ext JS, LLC.
11025  *
11026  * Originally Released Under LGPL - original licence link has changed is not relivant.
11027  *
11028  * Fork - LGPL
11029  * <script type="text/javascript">
11030  */
11031
11032 // as we use this in bootstrap.
11033 Roo.namespace('Roo.form');
11034  /**
11035  * @class Roo.form.Action
11036  * Internal Class used to handle form actions
11037  * @constructor
11038  * @param {Roo.form.BasicForm} el The form element or its id
11039  * @param {Object} config Configuration options
11040  */
11041
11042  
11043  
11044 // define the action interface
11045 Roo.form.Action = function(form, options){
11046     this.form = form;
11047     this.options = options || {};
11048 };
11049 /**
11050  * Client Validation Failed
11051  * @const 
11052  */
11053 Roo.form.Action.CLIENT_INVALID = 'client';
11054 /**
11055  * Server Validation Failed
11056  * @const 
11057  */
11058 Roo.form.Action.SERVER_INVALID = 'server';
11059  /**
11060  * Connect to Server Failed
11061  * @const 
11062  */
11063 Roo.form.Action.CONNECT_FAILURE = 'connect';
11064 /**
11065  * Reading Data from Server Failed
11066  * @const 
11067  */
11068 Roo.form.Action.LOAD_FAILURE = 'load';
11069
11070 Roo.form.Action.prototype = {
11071     type : 'default',
11072     failureType : undefined,
11073     response : undefined,
11074     result : undefined,
11075
11076     // interface method
11077     run : function(options){
11078
11079     },
11080
11081     // interface method
11082     success : function(response){
11083
11084     },
11085
11086     // interface method
11087     handleResponse : function(response){
11088
11089     },
11090
11091     // default connection failure
11092     failure : function(response){
11093         
11094         this.response = response;
11095         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11096         this.form.afterAction(this, false);
11097     },
11098
11099     processResponse : function(response){
11100         this.response = response;
11101         if(!response.responseText){
11102             return true;
11103         }
11104         this.result = this.handleResponse(response);
11105         return this.result;
11106     },
11107
11108     // utility functions used internally
11109     getUrl : function(appendParams){
11110         var url = this.options.url || this.form.url || this.form.el.dom.action;
11111         if(appendParams){
11112             var p = this.getParams();
11113             if(p){
11114                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11115             }
11116         }
11117         return url;
11118     },
11119
11120     getMethod : function(){
11121         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11122     },
11123
11124     getParams : function(){
11125         var bp = this.form.baseParams;
11126         var p = this.options.params;
11127         if(p){
11128             if(typeof p == "object"){
11129                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11130             }else if(typeof p == 'string' && bp){
11131                 p += '&' + Roo.urlEncode(bp);
11132             }
11133         }else if(bp){
11134             p = Roo.urlEncode(bp);
11135         }
11136         return p;
11137     },
11138
11139     createCallback : function(){
11140         return {
11141             success: this.success,
11142             failure: this.failure,
11143             scope: this,
11144             timeout: (this.form.timeout*1000),
11145             upload: this.form.fileUpload ? this.success : undefined
11146         };
11147     }
11148 };
11149
11150 Roo.form.Action.Submit = function(form, options){
11151     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11152 };
11153
11154 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11155     type : 'submit',
11156
11157     haveProgress : false,
11158     uploadComplete : false,
11159     
11160     // uploadProgress indicator.
11161     uploadProgress : function()
11162     {
11163         if (!this.form.progressUrl) {
11164             return;
11165         }
11166         
11167         if (!this.haveProgress) {
11168             Roo.MessageBox.progress("Uploading", "Uploading");
11169         }
11170         if (this.uploadComplete) {
11171            Roo.MessageBox.hide();
11172            return;
11173         }
11174         
11175         this.haveProgress = true;
11176    
11177         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11178         
11179         var c = new Roo.data.Connection();
11180         c.request({
11181             url : this.form.progressUrl,
11182             params: {
11183                 id : uid
11184             },
11185             method: 'GET',
11186             success : function(req){
11187                //console.log(data);
11188                 var rdata = false;
11189                 var edata;
11190                 try  {
11191                    rdata = Roo.decode(req.responseText)
11192                 } catch (e) {
11193                     Roo.log("Invalid data from server..");
11194                     Roo.log(edata);
11195                     return;
11196                 }
11197                 if (!rdata || !rdata.success) {
11198                     Roo.log(rdata);
11199                     Roo.MessageBox.alert(Roo.encode(rdata));
11200                     return;
11201                 }
11202                 var data = rdata.data;
11203                 
11204                 if (this.uploadComplete) {
11205                    Roo.MessageBox.hide();
11206                    return;
11207                 }
11208                    
11209                 if (data){
11210                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11211                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11212                     );
11213                 }
11214                 this.uploadProgress.defer(2000,this);
11215             },
11216        
11217             failure: function(data) {
11218                 Roo.log('progress url failed ');
11219                 Roo.log(data);
11220             },
11221             scope : this
11222         });
11223            
11224     },
11225     
11226     
11227     run : function()
11228     {
11229         // run get Values on the form, so it syncs any secondary forms.
11230         this.form.getValues();
11231         
11232         var o = this.options;
11233         var method = this.getMethod();
11234         var isPost = method == 'POST';
11235         if(o.clientValidation === false || this.form.isValid()){
11236             
11237             if (this.form.progressUrl) {
11238                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11239                     (new Date() * 1) + '' + Math.random());
11240                     
11241             } 
11242             
11243             
11244             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11245                 form:this.form.el.dom,
11246                 url:this.getUrl(!isPost),
11247                 method: method,
11248                 params:isPost ? this.getParams() : null,
11249                 isUpload: this.form.fileUpload,
11250                 formData : this.form.formData
11251             }));
11252             
11253             this.uploadProgress();
11254
11255         }else if (o.clientValidation !== false){ // client validation failed
11256             this.failureType = Roo.form.Action.CLIENT_INVALID;
11257             this.form.afterAction(this, false);
11258         }
11259     },
11260
11261     success : function(response)
11262     {
11263         this.uploadComplete= true;
11264         if (this.haveProgress) {
11265             Roo.MessageBox.hide();
11266         }
11267         
11268         
11269         var result = this.processResponse(response);
11270         if(result === true || result.success){
11271             this.form.afterAction(this, true);
11272             return;
11273         }
11274         if(result.errors){
11275             this.form.markInvalid(result.errors);
11276             this.failureType = Roo.form.Action.SERVER_INVALID;
11277         }
11278         this.form.afterAction(this, false);
11279     },
11280     failure : function(response)
11281     {
11282         this.uploadComplete= true;
11283         if (this.haveProgress) {
11284             Roo.MessageBox.hide();
11285         }
11286         
11287         this.response = response;
11288         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11289         this.form.afterAction(this, false);
11290     },
11291     
11292     handleResponse : function(response){
11293         if(this.form.errorReader){
11294             var rs = this.form.errorReader.read(response);
11295             var errors = [];
11296             if(rs.records){
11297                 for(var i = 0, len = rs.records.length; i < len; i++) {
11298                     var r = rs.records[i];
11299                     errors[i] = r.data;
11300                 }
11301             }
11302             if(errors.length < 1){
11303                 errors = null;
11304             }
11305             return {
11306                 success : rs.success,
11307                 errors : errors
11308             };
11309         }
11310         var ret = false;
11311         try {
11312             var rt = response.responseText;
11313             if (rt.match(/^\<!--\[CDATA\[/)) {
11314                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11315                 rt = rt.replace(/\]\]--\>$/,'');
11316             }
11317             
11318             ret = Roo.decode(rt);
11319         } catch (e) {
11320             ret = {
11321                 success: false,
11322                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11323                 errors : []
11324             };
11325         }
11326         return ret;
11327         
11328     }
11329 });
11330
11331
11332 Roo.form.Action.Load = function(form, options){
11333     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11334     this.reader = this.form.reader;
11335 };
11336
11337 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11338     type : 'load',
11339
11340     run : function(){
11341         
11342         Roo.Ajax.request(Roo.apply(
11343                 this.createCallback(), {
11344                     method:this.getMethod(),
11345                     url:this.getUrl(false),
11346                     params:this.getParams()
11347         }));
11348     },
11349
11350     success : function(response){
11351         
11352         var result = this.processResponse(response);
11353         if(result === true || !result.success || !result.data){
11354             this.failureType = Roo.form.Action.LOAD_FAILURE;
11355             this.form.afterAction(this, false);
11356             return;
11357         }
11358         this.form.clearInvalid();
11359         this.form.setValues(result.data);
11360         this.form.afterAction(this, true);
11361     },
11362
11363     handleResponse : function(response){
11364         if(this.form.reader){
11365             var rs = this.form.reader.read(response);
11366             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11367             return {
11368                 success : rs.success,
11369                 data : data
11370             };
11371         }
11372         return Roo.decode(response.responseText);
11373     }
11374 });
11375
11376 Roo.form.Action.ACTION_TYPES = {
11377     'load' : Roo.form.Action.Load,
11378     'submit' : Roo.form.Action.Submit
11379 };/*
11380  * - LGPL
11381  *
11382  * form
11383  *
11384  */
11385
11386 /**
11387  * @class Roo.bootstrap.form.Form
11388  * @extends Roo.bootstrap.Component
11389  * @children Roo.bootstrap.Component
11390  * Bootstrap Form class
11391  * @cfg {String} method  GET | POST (default POST)
11392  * @cfg {String} labelAlign top | left (default top)
11393  * @cfg {String} align left  | right - for navbars
11394  * @cfg {Boolean} loadMask load mask when submit (default true)
11395
11396  *
11397  * @constructor
11398  * Create a new Form
11399  * @param {Object} config The config object
11400  */
11401
11402
11403 Roo.bootstrap.form.Form = function(config){
11404     
11405     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11406     
11407     Roo.bootstrap.form.Form.popover.apply();
11408     
11409     this.addEvents({
11410         /**
11411          * @event clientvalidation
11412          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11413          * @param {Form} this
11414          * @param {Boolean} valid true if the form has passed client-side validation
11415          */
11416         clientvalidation: true,
11417         /**
11418          * @event beforeaction
11419          * Fires before any action is performed. Return false to cancel the action.
11420          * @param {Form} this
11421          * @param {Action} action The action to be performed
11422          */
11423         beforeaction: true,
11424         /**
11425          * @event actionfailed
11426          * Fires when an action fails.
11427          * @param {Form} this
11428          * @param {Action} action The action that failed
11429          */
11430         actionfailed : true,
11431         /**
11432          * @event actioncomplete
11433          * Fires when an action is completed.
11434          * @param {Form} this
11435          * @param {Action} action The action that completed
11436          */
11437         actioncomplete : true
11438     });
11439 };
11440
11441 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11442
11443      /**
11444      * @cfg {String} method
11445      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11446      */
11447     method : 'POST',
11448     /**
11449      * @cfg {String} url
11450      * The URL to use for form actions if one isn't supplied in the action options.
11451      */
11452     /**
11453      * @cfg {Boolean} fileUpload
11454      * Set to true if this form is a file upload.
11455      */
11456
11457     /**
11458      * @cfg {Object} baseParams
11459      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11460      */
11461
11462     /**
11463      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11464      */
11465     timeout: 30,
11466     /**
11467      * @cfg {Sting} align (left|right) for navbar forms
11468      */
11469     align : 'left',
11470
11471     // private
11472     activeAction : null,
11473
11474     /**
11475      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11476      * element by passing it or its id or mask the form itself by passing in true.
11477      * @type Mixed
11478      */
11479     waitMsgTarget : false,
11480
11481     loadMask : true,
11482     
11483     /**
11484      * @cfg {Boolean} errorMask (true|false) default false
11485      */
11486     errorMask : false,
11487     
11488     /**
11489      * @cfg {Number} maskOffset Default 100
11490      */
11491     maskOffset : 100,
11492     
11493     /**
11494      * @cfg {Boolean} maskBody
11495      */
11496     maskBody : false,
11497
11498     getAutoCreate : function(){
11499
11500         var cfg = {
11501             tag: 'form',
11502             method : this.method || 'POST',
11503             id : this.id || Roo.id(),
11504             cls : ''
11505         };
11506         if (this.parent().xtype.match(/^Nav/)) {
11507             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11508
11509         }
11510
11511         if (this.labelAlign == 'left' ) {
11512             cfg.cls += ' form-horizontal';
11513         }
11514
11515
11516         return cfg;
11517     },
11518     initEvents : function()
11519     {
11520         this.el.on('submit', this.onSubmit, this);
11521         // this was added as random key presses on the form where triggering form submit.
11522         this.el.on('keypress', function(e) {
11523             if (e.getCharCode() != 13) {
11524                 return true;
11525             }
11526             // we might need to allow it for textareas.. and some other items.
11527             // check e.getTarget().
11528
11529             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11530                 return true;
11531             }
11532
11533             Roo.log("keypress blocked");
11534
11535             e.preventDefault();
11536             return false;
11537         });
11538         
11539     },
11540     // private
11541     onSubmit : function(e){
11542         e.stopEvent();
11543     },
11544
11545      /**
11546      * Returns true if client-side validation on the form is successful.
11547      * @return Boolean
11548      */
11549     isValid : function(){
11550         var items = this.getItems();
11551         var valid = true;
11552         var target = false;
11553         
11554         items.each(function(f){
11555             
11556             if(f.validate()){
11557                 return;
11558             }
11559             
11560             Roo.log('invalid field: ' + f.name);
11561             
11562             valid = false;
11563
11564             if(!target && f.el.isVisible(true)){
11565                 target = f;
11566             }
11567            
11568         });
11569         
11570         if(this.errorMask && !valid){
11571             Roo.bootstrap.form.Form.popover.mask(this, target);
11572         }
11573         
11574         return valid;
11575     },
11576     
11577     /**
11578      * Returns true if any fields in this form have changed since their original load.
11579      * @return Boolean
11580      */
11581     isDirty : function(){
11582         var dirty = false;
11583         var items = this.getItems();
11584         items.each(function(f){
11585            if(f.isDirty()){
11586                dirty = true;
11587                return false;
11588            }
11589            return true;
11590         });
11591         return dirty;
11592     },
11593      /**
11594      * Performs a predefined action (submit or load) or custom actions you define on this form.
11595      * @param {String} actionName The name of the action type
11596      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11597      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11598      * accept other config options):
11599      * <pre>
11600 Property          Type             Description
11601 ----------------  ---------------  ----------------------------------------------------------------------------------
11602 url               String           The url for the action (defaults to the form's url)
11603 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11604 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11605 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11606                                    validate the form on the client (defaults to false)
11607      * </pre>
11608      * @return {BasicForm} this
11609      */
11610     doAction : function(action, options){
11611         if(typeof action == 'string'){
11612             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11613         }
11614         if(this.fireEvent('beforeaction', this, action) !== false){
11615             this.beforeAction(action);
11616             action.run.defer(100, action);
11617         }
11618         return this;
11619     },
11620
11621     // private
11622     beforeAction : function(action){
11623         var o = action.options;
11624         
11625         if(this.loadMask){
11626             
11627             if(this.maskBody){
11628                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11629             } else {
11630                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11631             }
11632         }
11633         // not really supported yet.. ??
11634
11635         //if(this.waitMsgTarget === true){
11636         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11637         //}else if(this.waitMsgTarget){
11638         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11639         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11640         //}else {
11641         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11642        // }
11643
11644     },
11645
11646     // private
11647     afterAction : function(action, success){
11648         this.activeAction = null;
11649         var o = action.options;
11650
11651         if(this.loadMask){
11652             
11653             if(this.maskBody){
11654                 Roo.get(document.body).unmask();
11655             } else {
11656                 this.el.unmask();
11657             }
11658         }
11659         
11660         //if(this.waitMsgTarget === true){
11661 //            this.el.unmask();
11662         //}else if(this.waitMsgTarget){
11663         //    this.waitMsgTarget.unmask();
11664         //}else{
11665         //    Roo.MessageBox.updateProgress(1);
11666         //    Roo.MessageBox.hide();
11667        // }
11668         //
11669         if(success){
11670             if(o.reset){
11671                 this.reset();
11672             }
11673             Roo.callback(o.success, o.scope, [this, action]);
11674             this.fireEvent('actioncomplete', this, action);
11675
11676         }else{
11677
11678             // failure condition..
11679             // we have a scenario where updates need confirming.
11680             // eg. if a locking scenario exists..
11681             // we look for { errors : { needs_confirm : true }} in the response.
11682             if (
11683                 (typeof(action.result) != 'undefined')  &&
11684                 (typeof(action.result.errors) != 'undefined')  &&
11685                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11686            ){
11687                 var _t = this;
11688                 Roo.log("not supported yet");
11689                  /*
11690
11691                 Roo.MessageBox.confirm(
11692                     "Change requires confirmation",
11693                     action.result.errorMsg,
11694                     function(r) {
11695                         if (r != 'yes') {
11696                             return;
11697                         }
11698                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11699                     }
11700
11701                 );
11702                 */
11703
11704
11705                 return;
11706             }
11707
11708             Roo.callback(o.failure, o.scope, [this, action]);
11709             // show an error message if no failed handler is set..
11710             if (!this.hasListener('actionfailed')) {
11711                 Roo.log("need to add dialog support");
11712                 /*
11713                 Roo.MessageBox.alert("Error",
11714                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11715                         action.result.errorMsg :
11716                         "Saving Failed, please check your entries or try again"
11717                 );
11718                 */
11719             }
11720
11721             this.fireEvent('actionfailed', this, action);
11722         }
11723
11724     },
11725     /**
11726      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11727      * @param {String} id The value to search for
11728      * @return Field
11729      */
11730     findField : function(id){
11731         var items = this.getItems();
11732         var field = items.get(id);
11733         if(!field){
11734              items.each(function(f){
11735                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11736                     field = f;
11737                     return false;
11738                 }
11739                 return true;
11740             });
11741         }
11742         return field || null;
11743     },
11744      /**
11745      * Mark fields in this form invalid in bulk.
11746      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11747      * @return {BasicForm} this
11748      */
11749     markInvalid : function(errors){
11750         if(errors instanceof Array){
11751             for(var i = 0, len = errors.length; i < len; i++){
11752                 var fieldError = errors[i];
11753                 var f = this.findField(fieldError.id);
11754                 if(f){
11755                     f.markInvalid(fieldError.msg);
11756                 }
11757             }
11758         }else{
11759             var field, id;
11760             for(id in errors){
11761                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11762                     field.markInvalid(errors[id]);
11763                 }
11764             }
11765         }
11766         //Roo.each(this.childForms || [], function (f) {
11767         //    f.markInvalid(errors);
11768         //});
11769
11770         return this;
11771     },
11772
11773     /**
11774      * Set values for fields in this form in bulk.
11775      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11776      * @return {BasicForm} this
11777      */
11778     setValues : function(values){
11779         if(values instanceof Array){ // array of objects
11780             for(var i = 0, len = values.length; i < len; i++){
11781                 var v = values[i];
11782                 var f = this.findField(v.id);
11783                 if(f){
11784                     f.setValue(v.value);
11785                     if(this.trackResetOnLoad){
11786                         f.originalValue = f.getValue();
11787                     }
11788                 }
11789             }
11790         }else{ // object hash
11791             var field, id;
11792             for(id in values){
11793                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11794
11795                     if (field.setFromData &&
11796                         field.valueField &&
11797                         field.displayField &&
11798                         // combos' with local stores can
11799                         // be queried via setValue()
11800                         // to set their value..
11801                         (field.store && !field.store.isLocal)
11802                         ) {
11803                         // it's a combo
11804                         var sd = { };
11805                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11806                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11807                         field.setFromData(sd);
11808
11809                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11810                         
11811                         field.setFromData(values);
11812                         
11813                     } else {
11814                         field.setValue(values[id]);
11815                     }
11816
11817
11818                     if(this.trackResetOnLoad){
11819                         field.originalValue = field.getValue();
11820                     }
11821                 }
11822             }
11823         }
11824
11825         //Roo.each(this.childForms || [], function (f) {
11826         //    f.setValues(values);
11827         //});
11828
11829         return this;
11830     },
11831
11832     /**
11833      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11834      * they are returned as an array.
11835      * @param {Boolean} asString
11836      * @return {Object}
11837      */
11838     getValues : function(asString){
11839         //if (this.childForms) {
11840             // copy values from the child forms
11841         //    Roo.each(this.childForms, function (f) {
11842         //        this.setValues(f.getValues());
11843         //    }, this);
11844         //}
11845
11846
11847
11848         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11849         if(asString === true){
11850             return fs;
11851         }
11852         return Roo.urlDecode(fs);
11853     },
11854
11855     /**
11856      * Returns the fields in this form as an object with key/value pairs.
11857      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11858      * @return {Object}
11859      */
11860     getFieldValues : function(with_hidden)
11861     {
11862         var items = this.getItems();
11863         var ret = {};
11864         items.each(function(f){
11865             
11866             if (!f.getName()) {
11867                 return;
11868             }
11869             
11870             var v = f.getValue();
11871             
11872             if (f.inputType =='radio') {
11873                 if (typeof(ret[f.getName()]) == 'undefined') {
11874                     ret[f.getName()] = ''; // empty..
11875                 }
11876
11877                 if (!f.el.dom.checked) {
11878                     return;
11879
11880                 }
11881                 v = f.el.dom.value;
11882
11883             }
11884             
11885             if(f.xtype == 'MoneyField'){
11886                 ret[f.currencyName] = f.getCurrency();
11887             }
11888
11889             // not sure if this supported any more..
11890             if ((typeof(v) == 'object') && f.getRawValue) {
11891                 v = f.getRawValue() ; // dates..
11892             }
11893             // combo boxes where name != hiddenName...
11894             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11895                 ret[f.name] = f.getRawValue();
11896             }
11897             ret[f.getName()] = v;
11898         });
11899
11900         return ret;
11901     },
11902
11903     /**
11904      * Clears all invalid messages in this form.
11905      * @return {BasicForm} this
11906      */
11907     clearInvalid : function(){
11908         var items = this.getItems();
11909
11910         items.each(function(f){
11911            f.clearInvalid();
11912         });
11913
11914         return this;
11915     },
11916
11917     /**
11918      * Resets this form.
11919      * @return {BasicForm} this
11920      */
11921     reset : function(){
11922         var items = this.getItems();
11923         items.each(function(f){
11924             f.reset();
11925         });
11926
11927         Roo.each(this.childForms || [], function (f) {
11928             f.reset();
11929         });
11930
11931
11932         return this;
11933     },
11934     
11935     getItems : function()
11936     {
11937         var r=new Roo.util.MixedCollection(false, function(o){
11938             return o.id || (o.id = Roo.id());
11939         });
11940         var iter = function(el) {
11941             if (el.inputEl) {
11942                 r.add(el);
11943             }
11944             if (!el.items) {
11945                 return;
11946             }
11947             Roo.each(el.items,function(e) {
11948                 iter(e);
11949             });
11950         };
11951
11952         iter(this);
11953         return r;
11954     },
11955     
11956     hideFields : function(items)
11957     {
11958         Roo.each(items, function(i){
11959             
11960             var f = this.findField(i);
11961             
11962             if(!f){
11963                 return;
11964             }
11965             
11966             f.hide();
11967             
11968         }, this);
11969     },
11970     
11971     showFields : function(items)
11972     {
11973         Roo.each(items, function(i){
11974             
11975             var f = this.findField(i);
11976             
11977             if(!f){
11978                 return;
11979             }
11980             
11981             f.show();
11982             
11983         }, this);
11984     }
11985
11986 });
11987
11988 Roo.apply(Roo.bootstrap.form.Form, {
11989     
11990     popover : {
11991         
11992         padding : 5,
11993         
11994         isApplied : false,
11995         
11996         isMasked : false,
11997         
11998         form : false,
11999         
12000         target : false,
12001         
12002         toolTip : false,
12003         
12004         intervalID : false,
12005         
12006         maskEl : false,
12007         
12008         apply : function()
12009         {
12010             if(this.isApplied){
12011                 return;
12012             }
12013             
12014             this.maskEl = {
12015                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12016                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12017                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12018                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12019             };
12020             
12021             this.maskEl.top.enableDisplayMode("block");
12022             this.maskEl.left.enableDisplayMode("block");
12023             this.maskEl.bottom.enableDisplayMode("block");
12024             this.maskEl.right.enableDisplayMode("block");
12025             
12026             this.toolTip = new Roo.bootstrap.Tooltip({
12027                 cls : 'roo-form-error-popover',
12028                 alignment : {
12029                     'left' : ['r-l', [-2,0], 'right'],
12030                     'right' : ['l-r', [2,0], 'left'],
12031                     'bottom' : ['tl-bl', [0,2], 'top'],
12032                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12033                 }
12034             });
12035             
12036             this.toolTip.render(Roo.get(document.body));
12037
12038             this.toolTip.el.enableDisplayMode("block");
12039             
12040             Roo.get(document.body).on('click', function(){
12041                 this.unmask();
12042             }, this);
12043             
12044             Roo.get(document.body).on('touchstart', function(){
12045                 this.unmask();
12046             }, this);
12047             
12048             this.isApplied = true
12049         },
12050         
12051         mask : function(form, target)
12052         {
12053             this.form = form;
12054             
12055             this.target = target;
12056             
12057             if(!this.form.errorMask || !target.el){
12058                 return;
12059             }
12060             
12061             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12062             
12063             Roo.log(scrollable);
12064             
12065             var ot = this.target.el.calcOffsetsTo(scrollable);
12066             
12067             var scrollTo = ot[1] - this.form.maskOffset;
12068             
12069             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12070             
12071             scrollable.scrollTo('top', scrollTo);
12072             
12073             var box = this.target.el.getBox();
12074             Roo.log(box);
12075             var zIndex = Roo.bootstrap.Modal.zIndex++;
12076
12077             
12078             this.maskEl.top.setStyle('position', 'absolute');
12079             this.maskEl.top.setStyle('z-index', zIndex);
12080             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12081             this.maskEl.top.setLeft(0);
12082             this.maskEl.top.setTop(0);
12083             this.maskEl.top.show();
12084             
12085             this.maskEl.left.setStyle('position', 'absolute');
12086             this.maskEl.left.setStyle('z-index', zIndex);
12087             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12088             this.maskEl.left.setLeft(0);
12089             this.maskEl.left.setTop(box.y - this.padding);
12090             this.maskEl.left.show();
12091
12092             this.maskEl.bottom.setStyle('position', 'absolute');
12093             this.maskEl.bottom.setStyle('z-index', zIndex);
12094             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12095             this.maskEl.bottom.setLeft(0);
12096             this.maskEl.bottom.setTop(box.bottom + this.padding);
12097             this.maskEl.bottom.show();
12098
12099             this.maskEl.right.setStyle('position', 'absolute');
12100             this.maskEl.right.setStyle('z-index', zIndex);
12101             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12102             this.maskEl.right.setLeft(box.right + this.padding);
12103             this.maskEl.right.setTop(box.y - this.padding);
12104             this.maskEl.right.show();
12105
12106             this.toolTip.bindEl = this.target.el;
12107
12108             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12109
12110             var tip = this.target.blankText;
12111
12112             if(this.target.getValue() !== '' ) {
12113                 
12114                 if (this.target.invalidText.length) {
12115                     tip = this.target.invalidText;
12116                 } else if (this.target.regexText.length){
12117                     tip = this.target.regexText;
12118                 }
12119             }
12120
12121             this.toolTip.show(tip);
12122
12123             this.intervalID = window.setInterval(function() {
12124                 Roo.bootstrap.form.Form.popover.unmask();
12125             }, 10000);
12126
12127             window.onwheel = function(){ return false;};
12128             
12129             (function(){ this.isMasked = true; }).defer(500, this);
12130             
12131         },
12132         
12133         unmask : function()
12134         {
12135             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12136                 return;
12137             }
12138             
12139             this.maskEl.top.setStyle('position', 'absolute');
12140             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12141             this.maskEl.top.hide();
12142
12143             this.maskEl.left.setStyle('position', 'absolute');
12144             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12145             this.maskEl.left.hide();
12146
12147             this.maskEl.bottom.setStyle('position', 'absolute');
12148             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12149             this.maskEl.bottom.hide();
12150
12151             this.maskEl.right.setStyle('position', 'absolute');
12152             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12153             this.maskEl.right.hide();
12154             
12155             this.toolTip.hide();
12156             
12157             this.toolTip.el.hide();
12158             
12159             window.onwheel = function(){ return true;};
12160             
12161             if(this.intervalID){
12162                 window.clearInterval(this.intervalID);
12163                 this.intervalID = false;
12164             }
12165             
12166             this.isMasked = false;
12167             
12168         }
12169         
12170     }
12171     
12172 });
12173
12174 /*
12175  * Based on:
12176  * Ext JS Library 1.1.1
12177  * Copyright(c) 2006-2007, Ext JS, LLC.
12178  *
12179  * Originally Released Under LGPL - original licence link has changed is not relivant.
12180  *
12181  * Fork - LGPL
12182  * <script type="text/javascript">
12183  */
12184 /**
12185  * @class Roo.form.VTypes
12186  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12187  * @static
12188  */
12189 Roo.form.VTypes = function(){
12190     // closure these in so they are only created once.
12191     var alpha = /^[a-zA-Z_]+$/;
12192     var alphanum = /^[a-zA-Z0-9_]+$/;
12193     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12194     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12195
12196     // All these messages and functions are configurable
12197     return {
12198         /**
12199          * The function used to validate email addresses
12200          * @param {String} value The email address
12201          */
12202         email : function(v){
12203             return email.test(v);
12204         },
12205         /**
12206          * The error text to display when the email validation function returns false
12207          * @type String
12208          */
12209         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12210         /**
12211          * The keystroke filter mask to be applied on email input
12212          * @type RegExp
12213          */
12214         emailMask : /[a-z0-9_\.\-@]/i,
12215
12216         /**
12217          * The function used to validate URLs
12218          * @param {String} value The URL
12219          */
12220         url : function(v){
12221             return url.test(v);
12222         },
12223         /**
12224          * The error text to display when the url validation function returns false
12225          * @type String
12226          */
12227         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12228         
12229         /**
12230          * The function used to validate alpha values
12231          * @param {String} value The value
12232          */
12233         alpha : function(v){
12234             return alpha.test(v);
12235         },
12236         /**
12237          * The error text to display when the alpha validation function returns false
12238          * @type String
12239          */
12240         alphaText : 'This field should only contain letters and _',
12241         /**
12242          * The keystroke filter mask to be applied on alpha input
12243          * @type RegExp
12244          */
12245         alphaMask : /[a-z_]/i,
12246
12247         /**
12248          * The function used to validate alphanumeric values
12249          * @param {String} value The value
12250          */
12251         alphanum : function(v){
12252             return alphanum.test(v);
12253         },
12254         /**
12255          * The error text to display when the alphanumeric validation function returns false
12256          * @type String
12257          */
12258         alphanumText : 'This field should only contain letters, numbers and _',
12259         /**
12260          * The keystroke filter mask to be applied on alphanumeric input
12261          * @type RegExp
12262          */
12263         alphanumMask : /[a-z0-9_]/i
12264     };
12265 }();/*
12266  * - LGPL
12267  *
12268  * Input
12269  * 
12270  */
12271
12272 /**
12273  * @class Roo.bootstrap.form.Input
12274  * @extends Roo.bootstrap.Component
12275  * Bootstrap Input class
12276  * @cfg {Boolean} disabled is it disabled
12277  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12278  * @cfg {String} name name of the input
12279  * @cfg {string} fieldLabel - the label associated
12280  * @cfg {string} placeholder - placeholder to put in text.
12281  * @cfg {string} before - input group add on before
12282  * @cfg {string} after - input group add on after
12283  * @cfg {string} size - (lg|sm) or leave empty..
12284  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12285  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12286  * @cfg {Number} md colspan out of 12 for computer-sized screens
12287  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12288  * @cfg {string} value default value of the input
12289  * @cfg {Number} labelWidth set the width of label 
12290  * @cfg {Number} labellg set the width of label (1-12)
12291  * @cfg {Number} labelmd set the width of label (1-12)
12292  * @cfg {Number} labelsm set the width of label (1-12)
12293  * @cfg {Number} labelxs set the width of label (1-12)
12294  * @cfg {String} labelAlign (top|left)
12295  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12296  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12297  * @cfg {String} indicatorpos (left|right) default left
12298  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12299  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12300  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12301  * @cfg {Roo.bootstrap.Button} before Button to show before
12302  * @cfg {Roo.bootstrap.Button} afterButton to show before
12303  * @cfg {String} align (left|center|right) Default left
12304  * @cfg {Boolean} forceFeedback (true|false) Default false
12305  * 
12306  * @constructor
12307  * Create a new Input
12308  * @param {Object} config The config object
12309  */
12310
12311 Roo.bootstrap.form.Input = function(config){
12312     
12313     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12314     
12315     this.addEvents({
12316         /**
12317          * @event focus
12318          * Fires when this field receives input focus.
12319          * @param {Roo.form.Field} this
12320          */
12321         focus : true,
12322         /**
12323          * @event blur
12324          * Fires when this field loses input focus.
12325          * @param {Roo.form.Field} this
12326          */
12327         blur : true,
12328         /**
12329          * @event specialkey
12330          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12331          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12332          * @param {Roo.form.Field} this
12333          * @param {Roo.EventObject} e The event object
12334          */
12335         specialkey : true,
12336         /**
12337          * @event change
12338          * Fires just before the field blurs if the field value has changed.
12339          * @param {Roo.form.Field} this
12340          * @param {Mixed} newValue The new value
12341          * @param {Mixed} oldValue The original value
12342          */
12343         change : true,
12344         /**
12345          * @event invalid
12346          * Fires after the field has been marked as invalid.
12347          * @param {Roo.form.Field} this
12348          * @param {String} msg The validation message
12349          */
12350         invalid : true,
12351         /**
12352          * @event valid
12353          * Fires after the field has been validated with no errors.
12354          * @param {Roo.form.Field} this
12355          */
12356         valid : true,
12357          /**
12358          * @event keyup
12359          * Fires after the key up
12360          * @param {Roo.form.Field} this
12361          * @param {Roo.EventObject}  e The event Object
12362          */
12363         keyup : true,
12364         /**
12365          * @event paste
12366          * Fires after the user pastes into input
12367          * @param {Roo.form.Field} this
12368          * @param {Roo.EventObject}  e The event Object
12369          */
12370         paste : true
12371     });
12372 };
12373
12374 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12375      /**
12376      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12377       automatic validation (defaults to "keyup").
12378      */
12379     validationEvent : "keyup",
12380      /**
12381      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12382      */
12383     validateOnBlur : true,
12384     /**
12385      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12386      */
12387     validationDelay : 250,
12388      /**
12389      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12390      */
12391     focusClass : "x-form-focus",  // not needed???
12392     
12393        
12394     /**
12395      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12396      */
12397     invalidClass : "has-warning",
12398     
12399     /**
12400      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12401      */
12402     validClass : "has-success",
12403     
12404     /**
12405      * @cfg {Boolean} hasFeedback (true|false) default true
12406      */
12407     hasFeedback : true,
12408     
12409     /**
12410      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12411      */
12412     invalidFeedbackClass : "glyphicon-warning-sign",
12413     
12414     /**
12415      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12416      */
12417     validFeedbackClass : "glyphicon-ok",
12418     
12419     /**
12420      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12421      */
12422     selectOnFocus : false,
12423     
12424      /**
12425      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12426      */
12427     maskRe : null,
12428        /**
12429      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12430      */
12431     vtype : null,
12432     
12433       /**
12434      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12435      */
12436     disableKeyFilter : false,
12437     
12438        /**
12439      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12440      */
12441     disabled : false,
12442      /**
12443      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12444      */
12445     allowBlank : true,
12446     /**
12447      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12448      */
12449     blankText : "Please complete this mandatory field",
12450     
12451      /**
12452      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12453      */
12454     minLength : 0,
12455     /**
12456      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12457      */
12458     maxLength : Number.MAX_VALUE,
12459     /**
12460      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12461      */
12462     minLengthText : "The minimum length for this field is {0}",
12463     /**
12464      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12465      */
12466     maxLengthText : "The maximum length for this field is {0}",
12467   
12468     
12469     /**
12470      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12471      * If available, this function will be called only after the basic validators all return true, and will be passed the
12472      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12473      */
12474     validator : null,
12475     /**
12476      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12477      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12478      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12479      */
12480     regex : null,
12481     /**
12482      * @cfg {String} regexText -- Depricated - use Invalid Text
12483      */
12484     regexText : "",
12485     
12486     /**
12487      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12488      */
12489     invalidText : "",
12490     
12491     
12492     
12493     autocomplete: false,
12494     
12495     
12496     fieldLabel : '',
12497     inputType : 'text',
12498     
12499     name : false,
12500     placeholder: false,
12501     before : false,
12502     after : false,
12503     size : false,
12504     hasFocus : false,
12505     preventMark: false,
12506     isFormField : true,
12507     value : '',
12508     labelWidth : 2,
12509     labelAlign : false,
12510     readOnly : false,
12511     align : false,
12512     formatedValue : false,
12513     forceFeedback : false,
12514     
12515     indicatorpos : 'left',
12516     
12517     labellg : 0,
12518     labelmd : 0,
12519     labelsm : 0,
12520     labelxs : 0,
12521     
12522     capture : '',
12523     accept : '',
12524     
12525     parentLabelAlign : function()
12526     {
12527         var parent = this;
12528         while (parent.parent()) {
12529             parent = parent.parent();
12530             if (typeof(parent.labelAlign) !='undefined') {
12531                 return parent.labelAlign;
12532             }
12533         }
12534         return 'left';
12535         
12536     },
12537     
12538     getAutoCreate : function()
12539     {
12540         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12541         
12542         var id = Roo.id();
12543         
12544         var cfg = {};
12545         
12546         if(this.inputType != 'hidden'){
12547             cfg.cls = 'form-group' //input-group
12548         }
12549         
12550         var input =  {
12551             tag: 'input',
12552             id : id,
12553             type : this.inputType,
12554             value : this.value,
12555             cls : 'form-control',
12556             placeholder : this.placeholder || '',
12557             autocomplete : this.autocomplete || 'new-password'
12558         };
12559         if (this.inputType == 'file') {
12560             input.style = 'overflow:hidden'; // why not in CSS?
12561         }
12562         
12563         if(this.capture.length){
12564             input.capture = this.capture;
12565         }
12566         
12567         if(this.accept.length){
12568             input.accept = this.accept + "/*";
12569         }
12570         
12571         if(this.align){
12572             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12573         }
12574         
12575         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12576             input.maxLength = this.maxLength;
12577         }
12578         
12579         if (this.disabled) {
12580             input.disabled=true;
12581         }
12582         
12583         if (this.readOnly) {
12584             input.readonly=true;
12585         }
12586         
12587         if (this.name) {
12588             input.name = this.name;
12589         }
12590         
12591         if (this.size) {
12592             input.cls += ' input-' + this.size;
12593         }
12594         
12595         var settings=this;
12596         ['xs','sm','md','lg'].map(function(size){
12597             if (settings[size]) {
12598                 cfg.cls += ' col-' + size + '-' + settings[size];
12599             }
12600         });
12601         
12602         var inputblock = input;
12603         
12604         var feedback = {
12605             tag: 'span',
12606             cls: 'glyphicon form-control-feedback'
12607         };
12608             
12609         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12610             
12611             inputblock = {
12612                 cls : 'has-feedback',
12613                 cn :  [
12614                     input,
12615                     feedback
12616                 ] 
12617             };  
12618         }
12619         
12620         if (this.before || this.after) {
12621             
12622             inputblock = {
12623                 cls : 'input-group',
12624                 cn :  [] 
12625             };
12626             
12627             if (this.before && typeof(this.before) == 'string') {
12628                 
12629                 inputblock.cn.push({
12630                     tag :'span',
12631                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12632                     html : this.before
12633                 });
12634             }
12635             if (this.before && typeof(this.before) == 'object') {
12636                 this.before = Roo.factory(this.before);
12637                 
12638                 inputblock.cn.push({
12639                     tag :'span',
12640                     cls : 'roo-input-before input-group-prepend   input-group-' +
12641                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12642                 });
12643             }
12644             
12645             inputblock.cn.push(input);
12646             
12647             if (this.after && typeof(this.after) == 'string') {
12648                 inputblock.cn.push({
12649                     tag :'span',
12650                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12651                     html : this.after
12652                 });
12653             }
12654             if (this.after && typeof(this.after) == 'object') {
12655                 this.after = Roo.factory(this.after);
12656                 
12657                 inputblock.cn.push({
12658                     tag :'span',
12659                     cls : 'roo-input-after input-group-append  input-group-' +
12660                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12661                 });
12662             }
12663             
12664             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12665                 inputblock.cls += ' has-feedback';
12666                 inputblock.cn.push(feedback);
12667             }
12668         };
12669         var indicator = {
12670             tag : 'i',
12671             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12672             tooltip : 'This field is required'
12673         };
12674         if (this.allowBlank ) {
12675             indicator.style = this.allowBlank ? ' display:none' : '';
12676         }
12677         if (align ==='left' && this.fieldLabel.length) {
12678             
12679             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12680             
12681             cfg.cn = [
12682                 indicator,
12683                 {
12684                     tag: 'label',
12685                     'for' :  id,
12686                     cls : 'control-label col-form-label',
12687                     html : this.fieldLabel
12688
12689                 },
12690                 {
12691                     cls : "", 
12692                     cn: [
12693                         inputblock
12694                     ]
12695                 }
12696             ];
12697             
12698             var labelCfg = cfg.cn[1];
12699             var contentCfg = cfg.cn[2];
12700             
12701             if(this.indicatorpos == 'right'){
12702                 cfg.cn = [
12703                     {
12704                         tag: 'label',
12705                         'for' :  id,
12706                         cls : 'control-label col-form-label',
12707                         cn : [
12708                             {
12709                                 tag : 'span',
12710                                 html : this.fieldLabel
12711                             },
12712                             indicator
12713                         ]
12714                     },
12715                     {
12716                         cls : "",
12717                         cn: [
12718                             inputblock
12719                         ]
12720                     }
12721
12722                 ];
12723                 
12724                 labelCfg = cfg.cn[0];
12725                 contentCfg = cfg.cn[1];
12726             
12727             }
12728             
12729             if(this.labelWidth > 12){
12730                 labelCfg.style = "width: " + this.labelWidth + 'px';
12731             }
12732             
12733             if(this.labelWidth < 13 && this.labelmd == 0){
12734                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12735             }
12736             
12737             if(this.labellg > 0){
12738                 labelCfg.cls += ' col-lg-' + this.labellg;
12739                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12740             }
12741             
12742             if(this.labelmd > 0){
12743                 labelCfg.cls += ' col-md-' + this.labelmd;
12744                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12745             }
12746             
12747             if(this.labelsm > 0){
12748                 labelCfg.cls += ' col-sm-' + this.labelsm;
12749                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12750             }
12751             
12752             if(this.labelxs > 0){
12753                 labelCfg.cls += ' col-xs-' + this.labelxs;
12754                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12755             }
12756             
12757             
12758         } else if ( this.fieldLabel.length) {
12759                 
12760             
12761             
12762             cfg.cn = [
12763                 {
12764                     tag : 'i',
12765                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12766                     tooltip : 'This field is required',
12767                     style : this.allowBlank ? ' display:none' : '' 
12768                 },
12769                 {
12770                     tag: 'label',
12771                    //cls : 'input-group-addon',
12772                     html : this.fieldLabel
12773
12774                 },
12775
12776                inputblock
12777
12778            ];
12779            
12780            if(this.indicatorpos == 'right'){
12781        
12782                 cfg.cn = [
12783                     {
12784                         tag: 'label',
12785                        //cls : 'input-group-addon',
12786                         html : this.fieldLabel
12787
12788                     },
12789                     {
12790                         tag : 'i',
12791                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12792                         tooltip : 'This field is required',
12793                         style : this.allowBlank ? ' display:none' : '' 
12794                     },
12795
12796                    inputblock
12797
12798                ];
12799
12800             }
12801
12802         } else {
12803             
12804             cfg.cn = [
12805
12806                     inputblock
12807
12808             ];
12809                 
12810                 
12811         };
12812         
12813         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12814            cfg.cls += ' navbar-form';
12815         }
12816         
12817         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12818             // on BS4 we do this only if not form 
12819             cfg.cls += ' navbar-form';
12820             cfg.tag = 'li';
12821         }
12822         
12823         return cfg;
12824         
12825     },
12826     /**
12827      * return the real input element.
12828      */
12829     inputEl: function ()
12830     {
12831         return this.el.select('input.form-control',true).first();
12832     },
12833     
12834     tooltipEl : function()
12835     {
12836         return this.inputEl();
12837     },
12838     
12839     indicatorEl : function()
12840     {
12841         if (Roo.bootstrap.version == 4) {
12842             return false; // not enabled in v4 yet.
12843         }
12844         
12845         var indicator = this.el.select('i.roo-required-indicator',true).first();
12846         
12847         if(!indicator){
12848             return false;
12849         }
12850         
12851         return indicator;
12852         
12853     },
12854     
12855     setDisabled : function(v)
12856     {
12857         var i  = this.inputEl().dom;
12858         if (!v) {
12859             i.removeAttribute('disabled');
12860             return;
12861             
12862         }
12863         i.setAttribute('disabled','true');
12864     },
12865     initEvents : function()
12866     {
12867           
12868         this.inputEl().on("keydown" , this.fireKey,  this);
12869         this.inputEl().on("focus", this.onFocus,  this);
12870         this.inputEl().on("blur", this.onBlur,  this);
12871         
12872         this.inputEl().relayEvent('keyup', this);
12873         this.inputEl().relayEvent('paste', this);
12874         
12875         this.indicator = this.indicatorEl();
12876         
12877         if(this.indicator){
12878             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12879         }
12880  
12881         // reference to original value for reset
12882         this.originalValue = this.getValue();
12883         //Roo.form.TextField.superclass.initEvents.call(this);
12884         if(this.validationEvent == 'keyup'){
12885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12886             this.inputEl().on('keyup', this.filterValidation, this);
12887         }
12888         else if(this.validationEvent !== false){
12889             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12890         }
12891         
12892         if(this.selectOnFocus){
12893             this.on("focus", this.preFocus, this);
12894             
12895         }
12896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12897             this.inputEl().on("keypress", this.filterKeys, this);
12898         } else {
12899             this.inputEl().relayEvent('keypress', this);
12900         }
12901        /* if(this.grow){
12902             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12903             this.el.on("click", this.autoSize,  this);
12904         }
12905         */
12906         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12907             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12908         }
12909         
12910         if (typeof(this.before) == 'object') {
12911             this.before.render(this.el.select('.roo-input-before',true).first());
12912         }
12913         if (typeof(this.after) == 'object') {
12914             this.after.render(this.el.select('.roo-input-after',true).first());
12915         }
12916         
12917         this.inputEl().on('change', this.onChange, this);
12918         
12919     },
12920     filterValidation : function(e){
12921         if(!e.isNavKeyPress()){
12922             this.validationTask.delay(this.validationDelay);
12923         }
12924     },
12925      /**
12926      * Validates the field value
12927      * @return {Boolean} True if the value is valid, else false
12928      */
12929     validate : function(){
12930         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12931         if(this.disabled || this.validateValue(this.getRawValue())){
12932             this.markValid();
12933             return true;
12934         }
12935         
12936         this.markInvalid();
12937         return false;
12938     },
12939     
12940     
12941     /**
12942      * Validates a value according to the field's validation rules and marks the field as invalid
12943      * if the validation fails
12944      * @param {Mixed} value The value to validate
12945      * @return {Boolean} True if the value is valid, else false
12946      */
12947     validateValue : function(value)
12948     {
12949         if(this.getVisibilityEl().hasClass('hidden')){
12950             return true;
12951         }
12952         
12953         if(value.length < 1)  { // if it's blank
12954             if(this.allowBlank){
12955                 return true;
12956             }
12957             return false;
12958         }
12959         
12960         if(value.length < this.minLength){
12961             return false;
12962         }
12963         if(value.length > this.maxLength){
12964             return false;
12965         }
12966         if(this.vtype){
12967             var vt = Roo.form.VTypes;
12968             if(!vt[this.vtype](value, this)){
12969                 return false;
12970             }
12971         }
12972         if(typeof this.validator == "function"){
12973             var msg = this.validator(value);
12974             if (typeof(msg) == 'string') {
12975                 this.invalidText = msg;
12976             }
12977             if(msg !== true){
12978                 return false;
12979             }
12980         }
12981         
12982         if(this.regex && !this.regex.test(value)){
12983             return false;
12984         }
12985         
12986         return true;
12987     },
12988     
12989      // private
12990     fireKey : function(e){
12991         //Roo.log('field ' + e.getKey());
12992         if(e.isNavKeyPress()){
12993             this.fireEvent("specialkey", this, e);
12994         }
12995     },
12996     focus : function (selectText){
12997         if(this.rendered){
12998             this.inputEl().focus();
12999             if(selectText === true){
13000                 this.inputEl().dom.select();
13001             }
13002         }
13003         return this;
13004     } ,
13005     
13006     onFocus : function(){
13007         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13008            // this.el.addClass(this.focusClass);
13009         }
13010         if(!this.hasFocus){
13011             this.hasFocus = true;
13012             this.startValue = this.getValue();
13013             this.fireEvent("focus", this);
13014         }
13015     },
13016     
13017     beforeBlur : Roo.emptyFn,
13018
13019     
13020     // private
13021     onBlur : function(){
13022         this.beforeBlur();
13023         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13024             //this.el.removeClass(this.focusClass);
13025         }
13026         this.hasFocus = false;
13027         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13028             this.validate();
13029         }
13030         var v = this.getValue();
13031         if(String(v) !== String(this.startValue)){
13032             this.fireEvent('change', this, v, this.startValue);
13033         }
13034         this.fireEvent("blur", this);
13035     },
13036     
13037     onChange : function(e)
13038     {
13039         var v = this.getValue();
13040         if(String(v) !== String(this.startValue)){
13041             this.fireEvent('change', this, v, this.startValue);
13042         }
13043         
13044     },
13045     
13046     /**
13047      * Resets the current field value to the originally loaded value and clears any validation messages
13048      */
13049     reset : function(){
13050         this.setValue(this.originalValue);
13051         this.validate();
13052     },
13053      /**
13054      * Returns the name of the field
13055      * @return {Mixed} name The name field
13056      */
13057     getName: function(){
13058         return this.name;
13059     },
13060      /**
13061      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13062      * @return {Mixed} value The field value
13063      */
13064     getValue : function(){
13065         
13066         var v = this.inputEl().getValue();
13067         
13068         return v;
13069     },
13070     /**
13071      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13072      * @return {Mixed} value The field value
13073      */
13074     getRawValue : function(){
13075         var v = this.inputEl().getValue();
13076         
13077         return v;
13078     },
13079     
13080     /**
13081      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13082      * @param {Mixed} value The value to set
13083      */
13084     setRawValue : function(v){
13085         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13086     },
13087     
13088     selectText : function(start, end){
13089         var v = this.getRawValue();
13090         if(v.length > 0){
13091             start = start === undefined ? 0 : start;
13092             end = end === undefined ? v.length : end;
13093             var d = this.inputEl().dom;
13094             if(d.setSelectionRange){
13095                 d.setSelectionRange(start, end);
13096             }else if(d.createTextRange){
13097                 var range = d.createTextRange();
13098                 range.moveStart("character", start);
13099                 range.moveEnd("character", v.length-end);
13100                 range.select();
13101             }
13102         }
13103     },
13104     
13105     /**
13106      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13107      * @param {Mixed} value The value to set
13108      */
13109     setValue : function(v){
13110         this.value = v;
13111         if(this.rendered){
13112             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13113             this.validate();
13114         }
13115     },
13116     
13117     /*
13118     processValue : function(value){
13119         if(this.stripCharsRe){
13120             var newValue = value.replace(this.stripCharsRe, '');
13121             if(newValue !== value){
13122                 this.setRawValue(newValue);
13123                 return newValue;
13124             }
13125         }
13126         return value;
13127     },
13128   */
13129     preFocus : function(){
13130         
13131         if(this.selectOnFocus){
13132             this.inputEl().dom.select();
13133         }
13134     },
13135     filterKeys : function(e){
13136         var k = e.getKey();
13137         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13138             return;
13139         }
13140         var c = e.getCharCode(), cc = String.fromCharCode(c);
13141         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13142             return;
13143         }
13144         if(!this.maskRe.test(cc)){
13145             e.stopEvent();
13146         }
13147     },
13148      /**
13149      * Clear any invalid styles/messages for this field
13150      */
13151     clearInvalid : function(){
13152         
13153         if(!this.el || this.preventMark){ // not rendered
13154             return;
13155         }
13156         
13157         
13158         this.el.removeClass([this.invalidClass, 'is-invalid']);
13159         
13160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13161             
13162             var feedback = this.el.select('.form-control-feedback', true).first();
13163             
13164             if(feedback){
13165                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13166             }
13167             
13168         }
13169         
13170         if(this.indicator){
13171             this.indicator.removeClass('visible');
13172             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13173         }
13174         
13175         this.fireEvent('valid', this);
13176     },
13177     
13178      /**
13179      * Mark this field as valid
13180      */
13181     markValid : function()
13182     {
13183         if(!this.el  || this.preventMark){ // not rendered...
13184             return;
13185         }
13186         
13187         this.el.removeClass([this.invalidClass, this.validClass]);
13188         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13189
13190         var feedback = this.el.select('.form-control-feedback', true).first();
13191             
13192         if(feedback){
13193             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13194         }
13195         
13196         if(this.indicator){
13197             this.indicator.removeClass('visible');
13198             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13199         }
13200         
13201         if(this.disabled){
13202             return;
13203         }
13204         
13205            
13206         if(this.allowBlank && !this.getRawValue().length){
13207             return;
13208         }
13209         if (Roo.bootstrap.version == 3) {
13210             this.el.addClass(this.validClass);
13211         } else {
13212             this.inputEl().addClass('is-valid');
13213         }
13214
13215         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13216             
13217             var feedback = this.el.select('.form-control-feedback', true).first();
13218             
13219             if(feedback){
13220                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13221                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13222             }
13223             
13224         }
13225         
13226         this.fireEvent('valid', this);
13227     },
13228     
13229      /**
13230      * Mark this field as invalid
13231      * @param {String} msg The validation message
13232      */
13233     markInvalid : function(msg)
13234     {
13235         if(!this.el  || this.preventMark){ // not rendered
13236             return;
13237         }
13238         
13239         this.el.removeClass([this.invalidClass, this.validClass]);
13240         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13241         
13242         var feedback = this.el.select('.form-control-feedback', true).first();
13243             
13244         if(feedback){
13245             this.el.select('.form-control-feedback', true).first().removeClass(
13246                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13247         }
13248
13249         if(this.disabled){
13250             return;
13251         }
13252         
13253         if(this.allowBlank && !this.getRawValue().length){
13254             return;
13255         }
13256         
13257         if(this.indicator){
13258             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13259             this.indicator.addClass('visible');
13260         }
13261         if (Roo.bootstrap.version == 3) {
13262             this.el.addClass(this.invalidClass);
13263         } else {
13264             this.inputEl().addClass('is-invalid');
13265         }
13266         
13267         
13268         
13269         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13270             
13271             var feedback = this.el.select('.form-control-feedback', true).first();
13272             
13273             if(feedback){
13274                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13275                 
13276                 if(this.getValue().length || this.forceFeedback){
13277                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13278                 }
13279                 
13280             }
13281             
13282         }
13283         
13284         this.fireEvent('invalid', this, msg);
13285     },
13286     // private
13287     SafariOnKeyDown : function(event)
13288     {
13289         // this is a workaround for a password hang bug on chrome/ webkit.
13290         if (this.inputEl().dom.type != 'password') {
13291             return;
13292         }
13293         
13294         var isSelectAll = false;
13295         
13296         if(this.inputEl().dom.selectionEnd > 0){
13297             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13298         }
13299         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13300             event.preventDefault();
13301             this.setValue('');
13302             return;
13303         }
13304         
13305         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13306             
13307             event.preventDefault();
13308             // this is very hacky as keydown always get's upper case.
13309             //
13310             var cc = String.fromCharCode(event.getCharCode());
13311             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13312             
13313         }
13314     },
13315     adjustWidth : function(tag, w){
13316         tag = tag.toLowerCase();
13317         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13318             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13319                 if(tag == 'input'){
13320                     return w + 2;
13321                 }
13322                 if(tag == 'textarea'){
13323                     return w-2;
13324                 }
13325             }else if(Roo.isOpera){
13326                 if(tag == 'input'){
13327                     return w + 2;
13328                 }
13329                 if(tag == 'textarea'){
13330                     return w-2;
13331                 }
13332             }
13333         }
13334         return w;
13335     },
13336     
13337     setFieldLabel : function(v)
13338     {
13339         if(!this.rendered){
13340             return;
13341         }
13342         
13343         if(this.indicatorEl()){
13344             var ar = this.el.select('label > span',true);
13345             
13346             if (ar.elements.length) {
13347                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13348                 this.fieldLabel = v;
13349                 return;
13350             }
13351             
13352             var br = this.el.select('label',true);
13353             
13354             if(br.elements.length) {
13355                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13356                 this.fieldLabel = v;
13357                 return;
13358             }
13359             
13360             Roo.log('Cannot Found any of label > span || label in input');
13361             return;
13362         }
13363         
13364         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13365         this.fieldLabel = v;
13366         
13367         
13368     }
13369 });
13370
13371  
13372 /*
13373  * - LGPL
13374  *
13375  * Input
13376  * 
13377  */
13378
13379 /**
13380  * @class Roo.bootstrap.form.TextArea
13381  * @extends Roo.bootstrap.form.Input
13382  * Bootstrap TextArea class
13383  * @cfg {Number} cols Specifies the visible width of a text area
13384  * @cfg {Number} rows Specifies the visible number of lines in a text area
13385  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13386  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13387  * @cfg {string} html text
13388  * 
13389  * @constructor
13390  * Create a new TextArea
13391  * @param {Object} config The config object
13392  */
13393
13394 Roo.bootstrap.form.TextArea = function(config){
13395     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13396    
13397 };
13398
13399 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13400      
13401     cols : false,
13402     rows : 5,
13403     readOnly : false,
13404     warp : 'soft',
13405     resize : false,
13406     value: false,
13407     html: false,
13408     
13409     getAutoCreate : function(){
13410         
13411         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13412         
13413         var id = Roo.id();
13414         
13415         var cfg = {};
13416         
13417         if(this.inputType != 'hidden'){
13418             cfg.cls = 'form-group' //input-group
13419         }
13420         
13421         var input =  {
13422             tag: 'textarea',
13423             id : id,
13424             warp : this.warp,
13425             rows : this.rows,
13426             value : this.value || '',
13427             html: this.html || '',
13428             cls : 'form-control',
13429             placeholder : this.placeholder || '' 
13430             
13431         };
13432         
13433         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13434             input.maxLength = this.maxLength;
13435         }
13436         
13437         if(this.resize){
13438             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13439         }
13440         
13441         if(this.cols){
13442             input.cols = this.cols;
13443         }
13444         
13445         if (this.readOnly) {
13446             input.readonly = true;
13447         }
13448         
13449         if (this.name) {
13450             input.name = this.name;
13451         }
13452         
13453         if (this.size) {
13454             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13455         }
13456         
13457         var settings=this;
13458         ['xs','sm','md','lg'].map(function(size){
13459             if (settings[size]) {
13460                 cfg.cls += ' col-' + size + '-' + settings[size];
13461             }
13462         });
13463         
13464         var inputblock = input;
13465         
13466         if(this.hasFeedback && !this.allowBlank){
13467             
13468             var feedback = {
13469                 tag: 'span',
13470                 cls: 'glyphicon form-control-feedback'
13471             };
13472
13473             inputblock = {
13474                 cls : 'has-feedback',
13475                 cn :  [
13476                     input,
13477                     feedback
13478                 ] 
13479             };  
13480         }
13481         
13482         
13483         if (this.before || this.after) {
13484             
13485             inputblock = {
13486                 cls : 'input-group',
13487                 cn :  [] 
13488             };
13489             if (this.before) {
13490                 inputblock.cn.push({
13491                     tag :'span',
13492                     cls : 'input-group-addon',
13493                     html : this.before
13494                 });
13495             }
13496             
13497             inputblock.cn.push(input);
13498             
13499             if(this.hasFeedback && !this.allowBlank){
13500                 inputblock.cls += ' has-feedback';
13501                 inputblock.cn.push(feedback);
13502             }
13503             
13504             if (this.after) {
13505                 inputblock.cn.push({
13506                     tag :'span',
13507                     cls : 'input-group-addon',
13508                     html : this.after
13509                 });
13510             }
13511             
13512         }
13513         
13514         if (align ==='left' && this.fieldLabel.length) {
13515             cfg.cn = [
13516                 {
13517                     tag: 'label',
13518                     'for' :  id,
13519                     cls : 'control-label',
13520                     html : this.fieldLabel
13521                 },
13522                 {
13523                     cls : "",
13524                     cn: [
13525                         inputblock
13526                     ]
13527                 }
13528
13529             ];
13530             
13531             if(this.labelWidth > 12){
13532                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13533             }
13534
13535             if(this.labelWidth < 13 && this.labelmd == 0){
13536                 this.labelmd = this.labelWidth;
13537             }
13538
13539             if(this.labellg > 0){
13540                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13541                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13542             }
13543
13544             if(this.labelmd > 0){
13545                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13546                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13547             }
13548
13549             if(this.labelsm > 0){
13550                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13551                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13552             }
13553
13554             if(this.labelxs > 0){
13555                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13556                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13557             }
13558             
13559         } else if ( this.fieldLabel.length) {
13560             cfg.cn = [
13561
13562                {
13563                    tag: 'label',
13564                    //cls : 'input-group-addon',
13565                    html : this.fieldLabel
13566
13567                },
13568
13569                inputblock
13570
13571            ];
13572
13573         } else {
13574
13575             cfg.cn = [
13576
13577                 inputblock
13578
13579             ];
13580                 
13581         }
13582         
13583         if (this.disabled) {
13584             input.disabled=true;
13585         }
13586         
13587         return cfg;
13588         
13589     },
13590     /**
13591      * return the real textarea element.
13592      */
13593     inputEl: function ()
13594     {
13595         return this.el.select('textarea.form-control',true).first();
13596     },
13597     
13598     /**
13599      * Clear any invalid styles/messages for this field
13600      */
13601     clearInvalid : function()
13602     {
13603         
13604         if(!this.el || this.preventMark){ // not rendered
13605             return;
13606         }
13607         
13608         var label = this.el.select('label', true).first();
13609         var icon = this.el.select('i.fa-star', true).first();
13610         
13611         if(label && icon){
13612             icon.remove();
13613         }
13614         this.el.removeClass( this.validClass);
13615         this.inputEl().removeClass('is-invalid');
13616          
13617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13618             
13619             var feedback = this.el.select('.form-control-feedback', true).first();
13620             
13621             if(feedback){
13622                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13623             }
13624             
13625         }
13626         
13627         this.fireEvent('valid', this);
13628     },
13629     
13630      /**
13631      * Mark this field as valid
13632      */
13633     markValid : function()
13634     {
13635         if(!this.el  || this.preventMark){ // not rendered
13636             return;
13637         }
13638         
13639         this.el.removeClass([this.invalidClass, this.validClass]);
13640         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13641         
13642         var feedback = this.el.select('.form-control-feedback', true).first();
13643             
13644         if(feedback){
13645             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13646         }
13647
13648         if(this.disabled || this.allowBlank){
13649             return;
13650         }
13651         
13652         var label = this.el.select('label', true).first();
13653         var icon = this.el.select('i.fa-star', true).first();
13654         
13655         if(label && icon){
13656             icon.remove();
13657         }
13658         if (Roo.bootstrap.version == 3) {
13659             this.el.addClass(this.validClass);
13660         } else {
13661             this.inputEl().addClass('is-valid');
13662         }
13663         
13664         
13665         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13666             
13667             var feedback = this.el.select('.form-control-feedback', true).first();
13668             
13669             if(feedback){
13670                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13671                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13672             }
13673             
13674         }
13675         
13676         this.fireEvent('valid', this);
13677     },
13678     
13679      /**
13680      * Mark this field as invalid
13681      * @param {String} msg The validation message
13682      */
13683     markInvalid : function(msg)
13684     {
13685         if(!this.el  || this.preventMark){ // not rendered
13686             return;
13687         }
13688         
13689         this.el.removeClass([this.invalidClass, this.validClass]);
13690         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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
13698         if(this.disabled || this.allowBlank){
13699             return;
13700         }
13701         
13702         var label = this.el.select('label', true).first();
13703         var icon = this.el.select('i.fa-star', true).first();
13704         
13705         if(!this.getValue().length && label && !icon){
13706             this.el.createChild({
13707                 tag : 'i',
13708                 cls : 'text-danger fa fa-lg fa-star',
13709                 tooltip : 'This field is required',
13710                 style : 'margin-right:5px;'
13711             }, label, true);
13712         }
13713         
13714         if (Roo.bootstrap.version == 3) {
13715             this.el.addClass(this.invalidClass);
13716         } else {
13717             this.inputEl().addClass('is-invalid');
13718         }
13719         
13720         // fixme ... this may be depricated need to test..
13721         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13722             
13723             var feedback = this.el.select('.form-control-feedback', true).first();
13724             
13725             if(feedback){
13726                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13727                 
13728                 if(this.getValue().length || this.forceFeedback){
13729                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13730                 }
13731                 
13732             }
13733             
13734         }
13735         
13736         this.fireEvent('invalid', this, msg);
13737     }
13738 });
13739
13740  
13741 /*
13742  * - LGPL
13743  *
13744  * trigger field - base class for combo..
13745  * 
13746  */
13747  
13748 /**
13749  * @class Roo.bootstrap.form.TriggerField
13750  * @extends Roo.bootstrap.form.Input
13751  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13752  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13753  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13754  * for which you can provide a custom implementation.  For example:
13755  * <pre><code>
13756 var trigger = new Roo.bootstrap.form.TriggerField();
13757 trigger.onTriggerClick = myTriggerFn;
13758 trigger.applyTo('my-field');
13759 </code></pre>
13760  *
13761  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13762  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13763  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13764  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13765  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13766
13767  * @constructor
13768  * Create a new TriggerField.
13769  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13770  * to the base TextField)
13771  */
13772 Roo.bootstrap.form.TriggerField = function(config){
13773     this.mimicing = false;
13774     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13775 };
13776
13777 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13778     /**
13779      * @cfg {String} triggerClass A CSS class to apply to the trigger
13780      */
13781      /**
13782      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13783      */
13784     hideTrigger:false,
13785
13786     /**
13787      * @cfg {Boolean} removable (true|false) special filter default false
13788      */
13789     removable : false,
13790     
13791     /** @cfg {Boolean} grow @hide */
13792     /** @cfg {Number} growMin @hide */
13793     /** @cfg {Number} growMax @hide */
13794
13795     /**
13796      * @hide 
13797      * @method
13798      */
13799     autoSize: Roo.emptyFn,
13800     // private
13801     monitorTab : true,
13802     // private
13803     deferHeight : true,
13804
13805     
13806     actionMode : 'wrap',
13807     
13808     caret : false,
13809     
13810     
13811     getAutoCreate : function(){
13812        
13813         var align = this.labelAlign || this.parentLabelAlign();
13814         
13815         var id = Roo.id();
13816         
13817         var cfg = {
13818             cls: 'form-group' //input-group
13819         };
13820         
13821         
13822         var input =  {
13823             tag: 'input',
13824             id : id,
13825             type : this.inputType,
13826             cls : 'form-control',
13827             autocomplete: 'new-password',
13828             placeholder : this.placeholder || '' 
13829             
13830         };
13831         if (this.name) {
13832             input.name = this.name;
13833         }
13834         if (this.size) {
13835             input.cls += ' input-' + this.size;
13836         }
13837         
13838         if (this.disabled) {
13839             input.disabled=true;
13840         }
13841         
13842         var inputblock = input;
13843         
13844         if(this.hasFeedback && !this.allowBlank){
13845             
13846             var feedback = {
13847                 tag: 'span',
13848                 cls: 'glyphicon form-control-feedback'
13849             };
13850             
13851             if(this.removable && !this.editable  ){
13852                 inputblock = {
13853                     cls : 'has-feedback',
13854                     cn :  [
13855                         inputblock,
13856                         {
13857                             tag: 'button',
13858                             html : 'x',
13859                             cls : 'roo-combo-removable-btn close'
13860                         },
13861                         feedback
13862                     ] 
13863                 };
13864             } else {
13865                 inputblock = {
13866                     cls : 'has-feedback',
13867                     cn :  [
13868                         inputblock,
13869                         feedback
13870                     ] 
13871                 };
13872             }
13873
13874         } else {
13875             if(this.removable && !this.editable ){
13876                 inputblock = {
13877                     cls : 'roo-removable',
13878                     cn :  [
13879                         inputblock,
13880                         {
13881                             tag: 'button',
13882                             html : 'x',
13883                             cls : 'roo-combo-removable-btn close'
13884                         }
13885                     ] 
13886                 };
13887             }
13888         }
13889         
13890         if (this.before || this.after) {
13891             
13892             inputblock = {
13893                 cls : 'input-group',
13894                 cn :  [] 
13895             };
13896             if (this.before) {
13897                 inputblock.cn.push({
13898                     tag :'span',
13899                     cls : 'input-group-addon input-group-prepend input-group-text',
13900                     html : this.before
13901                 });
13902             }
13903             
13904             inputblock.cn.push(input);
13905             
13906             if(this.hasFeedback && !this.allowBlank){
13907                 inputblock.cls += ' has-feedback';
13908                 inputblock.cn.push(feedback);
13909             }
13910             
13911             if (this.after) {
13912                 inputblock.cn.push({
13913                     tag :'span',
13914                     cls : 'input-group-addon input-group-append input-group-text',
13915                     html : this.after
13916                 });
13917             }
13918             
13919         };
13920         
13921       
13922         
13923         var ibwrap = inputblock;
13924         
13925         if(this.multiple){
13926             ibwrap = {
13927                 tag: 'ul',
13928                 cls: 'roo-select2-choices',
13929                 cn:[
13930                     {
13931                         tag: 'li',
13932                         cls: 'roo-select2-search-field',
13933                         cn: [
13934
13935                             inputblock
13936                         ]
13937                     }
13938                 ]
13939             };
13940                 
13941         }
13942         
13943         var combobox = {
13944             cls: 'roo-select2-container input-group',
13945             cn: [
13946                  {
13947                     tag: 'input',
13948                     type : 'hidden',
13949                     cls: 'form-hidden-field'
13950                 },
13951                 ibwrap
13952             ]
13953         };
13954         
13955         if(!this.multiple && this.showToggleBtn){
13956             
13957             var caret = {
13958                         tag: 'span',
13959                         cls: 'caret'
13960              };
13961             if (this.caret != false) {
13962                 caret = {
13963                      tag: 'i',
13964                      cls: 'fa fa-' + this.caret
13965                 };
13966                 
13967             }
13968             
13969             combobox.cn.push({
13970                 tag :'span',
13971                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13972                 cn : [
13973                     Roo.bootstrap.version == 3 ? caret : '',
13974                     {
13975                         tag: 'span',
13976                         cls: 'combobox-clear',
13977                         cn  : [
13978                             {
13979                                 tag : 'i',
13980                                 cls: 'icon-remove'
13981                             }
13982                         ]
13983                     }
13984                 ]
13985
13986             })
13987         }
13988         
13989         if(this.multiple){
13990             combobox.cls += ' roo-select2-container-multi';
13991         }
13992          var indicator = {
13993             tag : 'i',
13994             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13995             tooltip : 'This field is required'
13996         };
13997         if (Roo.bootstrap.version == 4) {
13998             indicator = {
13999                 tag : 'i',
14000                 style : 'display:none'
14001             };
14002         }
14003         
14004         
14005         if (align ==='left' && this.fieldLabel.length) {
14006             
14007             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14008
14009             cfg.cn = [
14010                 indicator,
14011                 {
14012                     tag: 'label',
14013                     'for' :  id,
14014                     cls : 'control-label',
14015                     html : this.fieldLabel
14016
14017                 },
14018                 {
14019                     cls : "", 
14020                     cn: [
14021                         combobox
14022                     ]
14023                 }
14024
14025             ];
14026             
14027             var labelCfg = cfg.cn[1];
14028             var contentCfg = cfg.cn[2];
14029             
14030             if(this.indicatorpos == 'right'){
14031                 cfg.cn = [
14032                     {
14033                         tag: 'label',
14034                         'for' :  id,
14035                         cls : 'control-label',
14036                         cn : [
14037                             {
14038                                 tag : 'span',
14039                                 html : this.fieldLabel
14040                             },
14041                             indicator
14042                         ]
14043                     },
14044                     {
14045                         cls : "", 
14046                         cn: [
14047                             combobox
14048                         ]
14049                     }
14050
14051                 ];
14052                 
14053                 labelCfg = cfg.cn[0];
14054                 contentCfg = cfg.cn[1];
14055             }
14056             
14057             if(this.labelWidth > 12){
14058                 labelCfg.style = "width: " + this.labelWidth + 'px';
14059             }
14060             
14061             if(this.labelWidth < 13 && this.labelmd == 0){
14062                 this.labelmd = this.labelWidth;
14063             }
14064             
14065             if(this.labellg > 0){
14066                 labelCfg.cls += ' col-lg-' + this.labellg;
14067                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14068             }
14069             
14070             if(this.labelmd > 0){
14071                 labelCfg.cls += ' col-md-' + this.labelmd;
14072                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14073             }
14074             
14075             if(this.labelsm > 0){
14076                 labelCfg.cls += ' col-sm-' + this.labelsm;
14077                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14078             }
14079             
14080             if(this.labelxs > 0){
14081                 labelCfg.cls += ' col-xs-' + this.labelxs;
14082                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14083             }
14084             
14085         } else if ( this.fieldLabel.length) {
14086 //                Roo.log(" label");
14087             cfg.cn = [
14088                 indicator,
14089                {
14090                    tag: 'label',
14091                    //cls : 'input-group-addon',
14092                    html : this.fieldLabel
14093
14094                },
14095
14096                combobox
14097
14098             ];
14099             
14100             if(this.indicatorpos == 'right'){
14101                 
14102                 cfg.cn = [
14103                     {
14104                        tag: 'label',
14105                        cn : [
14106                            {
14107                                tag : 'span',
14108                                html : this.fieldLabel
14109                            },
14110                            indicator
14111                        ]
14112
14113                     },
14114                     combobox
14115
14116                 ];
14117
14118             }
14119
14120         } else {
14121             
14122 //                Roo.log(" no label && no align");
14123                 cfg = combobox
14124                      
14125                 
14126         }
14127         
14128         var settings=this;
14129         ['xs','sm','md','lg'].map(function(size){
14130             if (settings[size]) {
14131                 cfg.cls += ' col-' + size + '-' + settings[size];
14132             }
14133         });
14134         
14135         return cfg;
14136         
14137     },
14138     
14139     
14140     
14141     // private
14142     onResize : function(w, h){
14143 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14144 //        if(typeof w == 'number'){
14145 //            var x = w - this.trigger.getWidth();
14146 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14147 //            this.trigger.setStyle('left', x+'px');
14148 //        }
14149     },
14150
14151     // private
14152     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14153
14154     // private
14155     getResizeEl : function(){
14156         return this.inputEl();
14157     },
14158
14159     // private
14160     getPositionEl : function(){
14161         return this.inputEl();
14162     },
14163
14164     // private
14165     alignErrorIcon : function(){
14166         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14167     },
14168
14169     // private
14170     initEvents : function(){
14171         
14172         this.createList();
14173         
14174         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14175         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14176         if(!this.multiple && this.showToggleBtn){
14177             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14178             if(this.hideTrigger){
14179                 this.trigger.setDisplayed(false);
14180             }
14181             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14182         }
14183         
14184         if(this.multiple){
14185             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14186         }
14187         
14188         if(this.removable && !this.editable && !this.tickable){
14189             var close = this.closeTriggerEl();
14190             
14191             if(close){
14192                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14193                 close.on('click', this.removeBtnClick, this, close);
14194             }
14195         }
14196         
14197         //this.trigger.addClassOnOver('x-form-trigger-over');
14198         //this.trigger.addClassOnClick('x-form-trigger-click');
14199         
14200         //if(!this.width){
14201         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14202         //}
14203     },
14204     
14205     closeTriggerEl : function()
14206     {
14207         var close = this.el.select('.roo-combo-removable-btn', true).first();
14208         return close ? close : false;
14209     },
14210     
14211     removeBtnClick : function(e, h, el)
14212     {
14213         e.preventDefault();
14214         
14215         if(this.fireEvent("remove", this) !== false){
14216             this.reset();
14217             this.fireEvent("afterremove", this)
14218         }
14219     },
14220     
14221     createList : function()
14222     {
14223         this.list = Roo.get(document.body).createChild({
14224             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14225             cls: 'typeahead typeahead-long dropdown-menu shadow',
14226             style: 'display:none'
14227         });
14228         
14229         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14230         
14231     },
14232
14233     // private
14234     initTrigger : function(){
14235        
14236     },
14237
14238     // private
14239     onDestroy : function(){
14240         if(this.trigger){
14241             this.trigger.removeAllListeners();
14242           //  this.trigger.remove();
14243         }
14244         //if(this.wrap){
14245         //    this.wrap.remove();
14246         //}
14247         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14248     },
14249
14250     // private
14251     onFocus : function(){
14252         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14253         /*
14254         if(!this.mimicing){
14255             this.wrap.addClass('x-trigger-wrap-focus');
14256             this.mimicing = true;
14257             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14258             if(this.monitorTab){
14259                 this.el.on("keydown", this.checkTab, this);
14260             }
14261         }
14262         */
14263     },
14264
14265     // private
14266     checkTab : function(e){
14267         if(e.getKey() == e.TAB){
14268             this.triggerBlur();
14269         }
14270     },
14271
14272     // private
14273     onBlur : function(){
14274         // do nothing
14275     },
14276
14277     // private
14278     mimicBlur : function(e, t){
14279         /*
14280         if(!this.wrap.contains(t) && this.validateBlur()){
14281             this.triggerBlur();
14282         }
14283         */
14284     },
14285
14286     // private
14287     triggerBlur : function(){
14288         this.mimicing = false;
14289         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14290         if(this.monitorTab){
14291             this.el.un("keydown", this.checkTab, this);
14292         }
14293         //this.wrap.removeClass('x-trigger-wrap-focus');
14294         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14295     },
14296
14297     // private
14298     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14299     validateBlur : function(e, t){
14300         return true;
14301     },
14302
14303     // private
14304     onDisable : function(){
14305         this.inputEl().dom.disabled = true;
14306         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14307         //if(this.wrap){
14308         //    this.wrap.addClass('x-item-disabled');
14309         //}
14310     },
14311
14312     // private
14313     onEnable : function(){
14314         this.inputEl().dom.disabled = false;
14315         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14316         //if(this.wrap){
14317         //    this.el.removeClass('x-item-disabled');
14318         //}
14319     },
14320
14321     // private
14322     onShow : function(){
14323         var ae = this.getActionEl();
14324         
14325         if(ae){
14326             ae.dom.style.display = '';
14327             ae.dom.style.visibility = 'visible';
14328         }
14329     },
14330
14331     // private
14332     
14333     onHide : function(){
14334         var ae = this.getActionEl();
14335         ae.dom.style.display = 'none';
14336     },
14337
14338     /**
14339      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14340      * by an implementing function.
14341      * @method
14342      * @param {EventObject} e
14343      */
14344     onTriggerClick : Roo.emptyFn
14345 });
14346  
14347 /*
14348 * Licence: LGPL
14349 */
14350
14351 /**
14352  * @class Roo.bootstrap.form.CardUploader
14353  * @extends Roo.bootstrap.Button
14354  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14355  * @cfg {Number} errorTimeout default 3000
14356  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14357  * @cfg {Array}  html The button text.
14358
14359  *
14360  * @constructor
14361  * Create a new CardUploader
14362  * @param {Object} config The config object
14363  */
14364
14365 Roo.bootstrap.form.CardUploader = function(config){
14366     
14367  
14368     
14369     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14370     
14371     
14372     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14373         return r.data.id
14374      });
14375     
14376      this.addEvents({
14377          // raw events
14378         /**
14379          * @event preview
14380          * When a image is clicked on - and needs to display a slideshow or similar..
14381          * @param {Roo.bootstrap.Card} this
14382          * @param {Object} The image information data 
14383          *
14384          */
14385         'preview' : true,
14386          /**
14387          * @event download
14388          * When a the download link is clicked
14389          * @param {Roo.bootstrap.Card} this
14390          * @param {Object} The image information data  contains 
14391          */
14392         'download' : true
14393         
14394     });
14395 };
14396  
14397 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14398     
14399      
14400     errorTimeout : 3000,
14401      
14402     images : false,
14403    
14404     fileCollection : false,
14405     allowBlank : true,
14406     
14407     getAutoCreate : function()
14408     {
14409         
14410         var cfg =  {
14411             cls :'form-group' ,
14412             cn : [
14413                
14414                 {
14415                     tag: 'label',
14416                    //cls : 'input-group-addon',
14417                     html : this.fieldLabel
14418
14419                 },
14420
14421                 {
14422                     tag: 'input',
14423                     type : 'hidden',
14424                     name : this.name,
14425                     value : this.value,
14426                     cls : 'd-none  form-control'
14427                 },
14428                 
14429                 {
14430                     tag: 'input',
14431                     multiple : 'multiple',
14432                     type : 'file',
14433                     cls : 'd-none  roo-card-upload-selector'
14434                 },
14435                 
14436                 {
14437                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14438                 },
14439                 {
14440                     cls : 'card-columns roo-card-uploader-container'
14441                 }
14442
14443             ]
14444         };
14445            
14446          
14447         return cfg;
14448     },
14449     
14450     getChildContainer : function() /// what children are added to.
14451     {
14452         return this.containerEl;
14453     },
14454    
14455     getButtonContainer : function() /// what children are added to.
14456     {
14457         return this.el.select(".roo-card-uploader-button-container").first();
14458     },
14459    
14460     initEvents : function()
14461     {
14462         
14463         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14464         
14465         var t = this;
14466         this.addxtype({
14467             xns: Roo.bootstrap,
14468
14469             xtype : 'Button',
14470             container_method : 'getButtonContainer' ,            
14471             html :  this.html, // fix changable?
14472             cls : 'w-100 ',
14473             listeners : {
14474                 'click' : function(btn, e) {
14475                     t.onClick(e);
14476                 }
14477             }
14478         });
14479         
14480         
14481         
14482         
14483         this.urlAPI = (window.createObjectURL && window) || 
14484                                 (window.URL && URL.revokeObjectURL && URL) || 
14485                                 (window.webkitURL && webkitURL);
14486                         
14487          
14488          
14489          
14490         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14491         
14492         this.selectorEl.on('change', this.onFileSelected, this);
14493         if (this.images) {
14494             var t = this;
14495             this.images.forEach(function(img) {
14496                 t.addCard(img)
14497             });
14498             this.images = false;
14499         }
14500         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14501          
14502        
14503     },
14504     
14505    
14506     onClick : function(e)
14507     {
14508         e.preventDefault();
14509          
14510         this.selectorEl.dom.click();
14511          
14512     },
14513     
14514     onFileSelected : function(e)
14515     {
14516         e.preventDefault();
14517         
14518         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14519             return;
14520         }
14521         
14522         Roo.each(this.selectorEl.dom.files, function(file){    
14523             this.addFile(file);
14524         }, this);
14525          
14526     },
14527     
14528       
14529     
14530       
14531     
14532     addFile : function(file)
14533     {
14534            
14535         if(typeof(file) === 'string'){
14536             throw "Add file by name?"; // should not happen
14537             return;
14538         }
14539         
14540         if(!file || !this.urlAPI){
14541             return;
14542         }
14543         
14544         // file;
14545         // file.type;
14546         
14547         var _this = this;
14548         
14549         
14550         var url = _this.urlAPI.createObjectURL( file);
14551            
14552         this.addCard({
14553             id : Roo.bootstrap.form.CardUploader.ID--,
14554             is_uploaded : false,
14555             src : url,
14556             srcfile : file,
14557             title : file.name,
14558             mimetype : file.type,
14559             preview : false,
14560             is_deleted : 0
14561         });
14562         
14563     },
14564     
14565     /**
14566      * addCard - add an Attachment to the uploader
14567      * @param data - the data about the image to upload
14568      *
14569      * {
14570           id : 123
14571           title : "Title of file",
14572           is_uploaded : false,
14573           src : "http://.....",
14574           srcfile : { the File upload object },
14575           mimetype : file.type,
14576           preview : false,
14577           is_deleted : 0
14578           .. any other data...
14579         }
14580      *
14581      * 
14582     */
14583     
14584     addCard : function (data)
14585     {
14586         // hidden input element?
14587         // if the file is not an image...
14588         //then we need to use something other that and header_image
14589         var t = this;
14590         //   remove.....
14591         var footer = [
14592             {
14593                 xns : Roo.bootstrap,
14594                 xtype : 'CardFooter',
14595                  items: [
14596                     {
14597                         xns : Roo.bootstrap,
14598                         xtype : 'Element',
14599                         cls : 'd-flex',
14600                         items : [
14601                             
14602                             {
14603                                 xns : Roo.bootstrap,
14604                                 xtype : 'Button',
14605                                 html : String.format("<small>{0}</small>", data.title),
14606                                 cls : 'col-10 text-left',
14607                                 size: 'sm',
14608                                 weight: 'link',
14609                                 fa : 'download',
14610                                 listeners : {
14611                                     click : function() {
14612                                      
14613                                         t.fireEvent( "download", t, data );
14614                                     }
14615                                 }
14616                             },
14617                           
14618                             {
14619                                 xns : Roo.bootstrap,
14620                                 xtype : 'Button',
14621                                 style: 'max-height: 28px; ',
14622                                 size : 'sm',
14623                                 weight: 'danger',
14624                                 cls : 'col-2',
14625                                 fa : 'times',
14626                                 listeners : {
14627                                     click : function() {
14628                                         t.removeCard(data.id)
14629                                     }
14630                                 }
14631                             }
14632                         ]
14633                     }
14634                     
14635                 ] 
14636             }
14637             
14638         ];
14639         
14640         var cn = this.addxtype(
14641             {
14642                  
14643                 xns : Roo.bootstrap,
14644                 xtype : 'Card',
14645                 closeable : true,
14646                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14647                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14648                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14649                 data : data,
14650                 html : false,
14651                  
14652                 items : footer,
14653                 initEvents : function() {
14654                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14655                     var card = this;
14656                     this.imgEl = this.el.select('.card-img-top').first();
14657                     if (this.imgEl) {
14658                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14659                         this.imgEl.set({ 'pointer' : 'cursor' });
14660                                   
14661                     }
14662                     this.getCardFooter().addClass('p-1');
14663                     
14664                   
14665                 }
14666                 
14667             }
14668         );
14669         // dont' really need ot update items.
14670         // this.items.push(cn);
14671         this.fileCollection.add(cn);
14672         
14673         if (!data.srcfile) {
14674             this.updateInput();
14675             return;
14676         }
14677             
14678         var _t = this;
14679         var reader = new FileReader();
14680         reader.addEventListener("load", function() {  
14681             data.srcdata =  reader.result;
14682             _t.updateInput();
14683         });
14684         reader.readAsDataURL(data.srcfile);
14685         
14686         
14687         
14688     },
14689     removeCard : function(id)
14690     {
14691         
14692         var card  = this.fileCollection.get(id);
14693         card.data.is_deleted = 1;
14694         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14695         //this.fileCollection.remove(card);
14696         //this.items = this.items.filter(function(e) { return e != card });
14697         // dont' really need ot update items.
14698         card.el.dom.parentNode.removeChild(card.el.dom);
14699         this.updateInput();
14700
14701         
14702     },
14703     reset: function()
14704     {
14705         this.fileCollection.each(function(card) {
14706             if (card.el.dom && card.el.dom.parentNode) {
14707                 card.el.dom.parentNode.removeChild(card.el.dom);
14708             }
14709         });
14710         this.fileCollection.clear();
14711         this.updateInput();
14712     },
14713     
14714     updateInput : function()
14715     {
14716          var data = [];
14717         this.fileCollection.each(function(e) {
14718             data.push(e.data);
14719             
14720         });
14721         this.inputEl().dom.value = JSON.stringify(data);
14722         
14723         
14724         
14725     }
14726     
14727     
14728 });
14729
14730
14731 Roo.bootstrap.form.CardUploader.ID = -1;/*
14732  * Based on:
14733  * Ext JS Library 1.1.1
14734  * Copyright(c) 2006-2007, Ext JS, LLC.
14735  *
14736  * Originally Released Under LGPL - original licence link has changed is not relivant.
14737  *
14738  * Fork - LGPL
14739  * <script type="text/javascript">
14740  */
14741
14742
14743 /**
14744  * @class Roo.data.SortTypes
14745  * @static
14746  * Defines the default sorting (casting?) comparison functions used when sorting data.
14747  */
14748 Roo.data.SortTypes = {
14749     /**
14750      * Default sort that does nothing
14751      * @param {Mixed} s The value being converted
14752      * @return {Mixed} The comparison value
14753      */
14754     none : function(s){
14755         return s;
14756     },
14757     
14758     /**
14759      * The regular expression used to strip tags
14760      * @type {RegExp}
14761      * @property
14762      */
14763     stripTagsRE : /<\/?[^>]+>/gi,
14764     
14765     /**
14766      * Strips all HTML tags to sort on text only
14767      * @param {Mixed} s The value being converted
14768      * @return {String} The comparison value
14769      */
14770     asText : function(s){
14771         return String(s).replace(this.stripTagsRE, "");
14772     },
14773     
14774     /**
14775      * Strips all HTML tags to sort on text only - Case insensitive
14776      * @param {Mixed} s The value being converted
14777      * @return {String} The comparison value
14778      */
14779     asUCText : function(s){
14780         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14781     },
14782     
14783     /**
14784      * Case insensitive string
14785      * @param {Mixed} s The value being converted
14786      * @return {String} The comparison value
14787      */
14788     asUCString : function(s) {
14789         return String(s).toUpperCase();
14790     },
14791     
14792     /**
14793      * Date sorting
14794      * @param {Mixed} s The value being converted
14795      * @return {Number} The comparison value
14796      */
14797     asDate : function(s) {
14798         if(!s){
14799             return 0;
14800         }
14801         if(s instanceof Date){
14802             return s.getTime();
14803         }
14804         return Date.parse(String(s));
14805     },
14806     
14807     /**
14808      * Float sorting
14809      * @param {Mixed} s The value being converted
14810      * @return {Float} The comparison value
14811      */
14812     asFloat : function(s) {
14813         var val = parseFloat(String(s).replace(/,/g, ""));
14814         if(isNaN(val)) {
14815             val = 0;
14816         }
14817         return val;
14818     },
14819     
14820     /**
14821      * Integer sorting
14822      * @param {Mixed} s The value being converted
14823      * @return {Number} The comparison value
14824      */
14825     asInt : function(s) {
14826         var val = parseInt(String(s).replace(/,/g, ""));
14827         if(isNaN(val)) {
14828             val = 0;
14829         }
14830         return val;
14831     }
14832 };/*
14833  * Based on:
14834  * Ext JS Library 1.1.1
14835  * Copyright(c) 2006-2007, Ext JS, LLC.
14836  *
14837  * Originally Released Under LGPL - original licence link has changed is not relivant.
14838  *
14839  * Fork - LGPL
14840  * <script type="text/javascript">
14841  */
14842
14843 /**
14844 * @class Roo.data.Record
14845  * Instances of this class encapsulate both record <em>definition</em> information, and record
14846  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14847  * to access Records cached in an {@link Roo.data.Store} object.<br>
14848  * <p>
14849  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14850  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14851  * objects.<br>
14852  * <p>
14853  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14854  * @constructor
14855  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14856  * {@link #create}. The parameters are the same.
14857  * @param {Array} data An associative Array of data values keyed by the field name.
14858  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14859  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14860  * not specified an integer id is generated.
14861  */
14862 Roo.data.Record = function(data, id){
14863     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14864     this.data = data;
14865 };
14866
14867 /**
14868  * Generate a constructor for a specific record layout.
14869  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14870  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14871  * Each field definition object may contain the following properties: <ul>
14872  * <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,
14873  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14874  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14875  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14876  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14877  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14878  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14879  * this may be omitted.</p></li>
14880  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14881  * <ul><li>auto (Default, implies no conversion)</li>
14882  * <li>string</li>
14883  * <li>int</li>
14884  * <li>float</li>
14885  * <li>boolean</li>
14886  * <li>date</li></ul></p></li>
14887  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14888  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14889  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14890  * by the Reader into an object that will be stored in the Record. It is passed the
14891  * following parameters:<ul>
14892  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14893  * </ul></p></li>
14894  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14895  * </ul>
14896  * <br>usage:<br><pre><code>
14897 var TopicRecord = Roo.data.Record.create(
14898     {name: 'title', mapping: 'topic_title'},
14899     {name: 'author', mapping: 'username'},
14900     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14901     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14902     {name: 'lastPoster', mapping: 'user2'},
14903     {name: 'excerpt', mapping: 'post_text'}
14904 );
14905
14906 var myNewRecord = new TopicRecord({
14907     title: 'Do my job please',
14908     author: 'noobie',
14909     totalPosts: 1,
14910     lastPost: new Date(),
14911     lastPoster: 'Animal',
14912     excerpt: 'No way dude!'
14913 });
14914 myStore.add(myNewRecord);
14915 </code></pre>
14916  * @method create
14917  * @static
14918  */
14919 Roo.data.Record.create = function(o){
14920     var f = function(){
14921         f.superclass.constructor.apply(this, arguments);
14922     };
14923     Roo.extend(f, Roo.data.Record);
14924     var p = f.prototype;
14925     p.fields = new Roo.util.MixedCollection(false, function(field){
14926         return field.name;
14927     });
14928     for(var i = 0, len = o.length; i < len; i++){
14929         p.fields.add(new Roo.data.Field(o[i]));
14930     }
14931     f.getField = function(name){
14932         return p.fields.get(name);  
14933     };
14934     return f;
14935 };
14936
14937 Roo.data.Record.AUTO_ID = 1000;
14938 Roo.data.Record.EDIT = 'edit';
14939 Roo.data.Record.REJECT = 'reject';
14940 Roo.data.Record.COMMIT = 'commit';
14941
14942 Roo.data.Record.prototype = {
14943     /**
14944      * Readonly flag - true if this record has been modified.
14945      * @type Boolean
14946      */
14947     dirty : false,
14948     editing : false,
14949     error: null,
14950     modified: null,
14951
14952     // private
14953     join : function(store){
14954         this.store = store;
14955     },
14956
14957     /**
14958      * Set the named field to the specified value.
14959      * @param {String} name The name of the field to set.
14960      * @param {Object} value The value to set the field to.
14961      */
14962     set : function(name, value){
14963         if(this.data[name] == value){
14964             return;
14965         }
14966         this.dirty = true;
14967         if(!this.modified){
14968             this.modified = {};
14969         }
14970         if(typeof this.modified[name] == 'undefined'){
14971             this.modified[name] = this.data[name];
14972         }
14973         this.data[name] = value;
14974         if(!this.editing && this.store){
14975             this.store.afterEdit(this);
14976         }       
14977     },
14978
14979     /**
14980      * Get the value of the named field.
14981      * @param {String} name The name of the field to get the value of.
14982      * @return {Object} The value of the field.
14983      */
14984     get : function(name){
14985         return this.data[name]; 
14986     },
14987
14988     // private
14989     beginEdit : function(){
14990         this.editing = true;
14991         this.modified = {}; 
14992     },
14993
14994     // private
14995     cancelEdit : function(){
14996         this.editing = false;
14997         delete this.modified;
14998     },
14999
15000     // private
15001     endEdit : function(){
15002         this.editing = false;
15003         if(this.dirty && this.store){
15004             this.store.afterEdit(this);
15005         }
15006     },
15007
15008     /**
15009      * Usually called by the {@link Roo.data.Store} which owns the Record.
15010      * Rejects all changes made to the Record since either creation, or the last commit operation.
15011      * Modified fields are reverted to their original values.
15012      * <p>
15013      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15014      * of reject operations.
15015      */
15016     reject : function(){
15017         var m = this.modified;
15018         for(var n in m){
15019             if(typeof m[n] != "function"){
15020                 this.data[n] = m[n];
15021             }
15022         }
15023         this.dirty = false;
15024         delete this.modified;
15025         this.editing = false;
15026         if(this.store){
15027             this.store.afterReject(this);
15028         }
15029     },
15030
15031     /**
15032      * Usually called by the {@link Roo.data.Store} which owns the Record.
15033      * Commits all changes made to the Record since either creation, or the last commit operation.
15034      * <p>
15035      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15036      * of commit operations.
15037      */
15038     commit : function(){
15039         this.dirty = false;
15040         delete this.modified;
15041         this.editing = false;
15042         if(this.store){
15043             this.store.afterCommit(this);
15044         }
15045     },
15046
15047     // private
15048     hasError : function(){
15049         return this.error != null;
15050     },
15051
15052     // private
15053     clearError : function(){
15054         this.error = null;
15055     },
15056
15057     /**
15058      * Creates a copy of this record.
15059      * @param {String} id (optional) A new record id if you don't want to use this record's id
15060      * @return {Record}
15061      */
15062     copy : function(newId) {
15063         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15064     }
15065 };/*
15066  * Based on:
15067  * Ext JS Library 1.1.1
15068  * Copyright(c) 2006-2007, Ext JS, LLC.
15069  *
15070  * Originally Released Under LGPL - original licence link has changed is not relivant.
15071  *
15072  * Fork - LGPL
15073  * <script type="text/javascript">
15074  */
15075
15076
15077
15078 /**
15079  * @class Roo.data.Store
15080  * @extends Roo.util.Observable
15081  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15082  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15083  * <p>
15084  * 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
15085  * has no knowledge of the format of the data returned by the Proxy.<br>
15086  * <p>
15087  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15088  * instances from the data object. These records are cached and made available through accessor functions.
15089  * @constructor
15090  * Creates a new Store.
15091  * @param {Object} config A config object containing the objects needed for the Store to access data,
15092  * and read the data into Records.
15093  */
15094 Roo.data.Store = function(config){
15095     this.data = new Roo.util.MixedCollection(false);
15096     this.data.getKey = function(o){
15097         return o.id;
15098     };
15099     this.baseParams = {};
15100     // private
15101     this.paramNames = {
15102         "start" : "start",
15103         "limit" : "limit",
15104         "sort" : "sort",
15105         "dir" : "dir",
15106         "multisort" : "_multisort"
15107     };
15108
15109     if(config && config.data){
15110         this.inlineData = config.data;
15111         delete config.data;
15112     }
15113
15114     Roo.apply(this, config);
15115     
15116     if(this.reader){ // reader passed
15117         this.reader = Roo.factory(this.reader, Roo.data);
15118         this.reader.xmodule = this.xmodule || false;
15119         if(!this.recordType){
15120             this.recordType = this.reader.recordType;
15121         }
15122         if(this.reader.onMetaChange){
15123             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15124         }
15125     }
15126
15127     if(this.recordType){
15128         this.fields = this.recordType.prototype.fields;
15129     }
15130     this.modified = [];
15131
15132     this.addEvents({
15133         /**
15134          * @event datachanged
15135          * Fires when the data cache has changed, and a widget which is using this Store
15136          * as a Record cache should refresh its view.
15137          * @param {Store} this
15138          */
15139         datachanged : true,
15140         /**
15141          * @event metachange
15142          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15143          * @param {Store} this
15144          * @param {Object} meta The JSON metadata
15145          */
15146         metachange : true,
15147         /**
15148          * @event add
15149          * Fires when Records have been added to the Store
15150          * @param {Store} this
15151          * @param {Roo.data.Record[]} records The array of Records added
15152          * @param {Number} index The index at which the record(s) were added
15153          */
15154         add : true,
15155         /**
15156          * @event remove
15157          * Fires when a Record has been removed from the Store
15158          * @param {Store} this
15159          * @param {Roo.data.Record} record The Record that was removed
15160          * @param {Number} index The index at which the record was removed
15161          */
15162         remove : true,
15163         /**
15164          * @event update
15165          * Fires when a Record has been updated
15166          * @param {Store} this
15167          * @param {Roo.data.Record} record The Record that was updated
15168          * @param {String} operation The update operation being performed.  Value may be one of:
15169          * <pre><code>
15170  Roo.data.Record.EDIT
15171  Roo.data.Record.REJECT
15172  Roo.data.Record.COMMIT
15173          * </code></pre>
15174          */
15175         update : true,
15176         /**
15177          * @event clear
15178          * Fires when the data cache has been cleared.
15179          * @param {Store} this
15180          */
15181         clear : true,
15182         /**
15183          * @event beforeload
15184          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15185          * the load action will be canceled.
15186          * @param {Store} this
15187          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15188          */
15189         beforeload : true,
15190         /**
15191          * @event beforeloadadd
15192          * Fires after a new set of Records has been loaded.
15193          * @param {Store} this
15194          * @param {Roo.data.Record[]} records The Records that were loaded
15195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15196          */
15197         beforeloadadd : true,
15198         /**
15199          * @event load
15200          * Fires after a new set of Records has been loaded, before they are added to the store.
15201          * @param {Store} this
15202          * @param {Roo.data.Record[]} records The Records that were loaded
15203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15204          * @params {Object} return from reader
15205          */
15206         load : true,
15207         /**
15208          * @event loadexception
15209          * Fires if an exception occurs in the Proxy during loading.
15210          * Called with the signature of the Proxy's "loadexception" event.
15211          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15212          * 
15213          * @param {Proxy} 
15214          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15215          * @param {Object} load options 
15216          * @param {Object} jsonData from your request (normally this contains the Exception)
15217          */
15218         loadexception : true
15219     });
15220     
15221     if(this.proxy){
15222         this.proxy = Roo.factory(this.proxy, Roo.data);
15223         this.proxy.xmodule = this.xmodule || false;
15224         this.relayEvents(this.proxy,  ["loadexception"]);
15225     }
15226     this.sortToggle = {};
15227     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15228
15229     Roo.data.Store.superclass.constructor.call(this);
15230
15231     if(this.inlineData){
15232         this.loadData(this.inlineData);
15233         delete this.inlineData;
15234     }
15235 };
15236
15237 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15238      /**
15239     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15240     * without a remote query - used by combo/forms at present.
15241     */
15242     
15243     /**
15244     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15245     */
15246     /**
15247     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15248     */
15249     /**
15250     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15251     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15252     */
15253     /**
15254     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15255     * on any HTTP request
15256     */
15257     /**
15258     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15259     */
15260     /**
15261     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15262     */
15263     multiSort: false,
15264     /**
15265     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15266     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15267     */
15268     remoteSort : false,
15269
15270     /**
15271     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15272      * loaded or when a record is removed. (defaults to false).
15273     */
15274     pruneModifiedRecords : false,
15275
15276     // private
15277     lastOptions : null,
15278
15279     /**
15280      * Add Records to the Store and fires the add event.
15281      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15282      */
15283     add : function(records){
15284         records = [].concat(records);
15285         for(var i = 0, len = records.length; i < len; i++){
15286             records[i].join(this);
15287         }
15288         var index = this.data.length;
15289         this.data.addAll(records);
15290         this.fireEvent("add", this, records, index);
15291     },
15292
15293     /**
15294      * Remove a Record from the Store and fires the remove event.
15295      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15296      */
15297     remove : function(record){
15298         var index = this.data.indexOf(record);
15299         this.data.removeAt(index);
15300  
15301         if(this.pruneModifiedRecords){
15302             this.modified.remove(record);
15303         }
15304         this.fireEvent("remove", this, record, index);
15305     },
15306
15307     /**
15308      * Remove all Records from the Store and fires the clear event.
15309      */
15310     removeAll : function(){
15311         this.data.clear();
15312         if(this.pruneModifiedRecords){
15313             this.modified = [];
15314         }
15315         this.fireEvent("clear", this);
15316     },
15317
15318     /**
15319      * Inserts Records to the Store at the given index and fires the add event.
15320      * @param {Number} index The start index at which to insert the passed Records.
15321      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15322      */
15323     insert : function(index, records){
15324         records = [].concat(records);
15325         for(var i = 0, len = records.length; i < len; i++){
15326             this.data.insert(index, records[i]);
15327             records[i].join(this);
15328         }
15329         this.fireEvent("add", this, records, index);
15330     },
15331
15332     /**
15333      * Get the index within the cache of the passed Record.
15334      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15335      * @return {Number} The index of the passed Record. Returns -1 if not found.
15336      */
15337     indexOf : function(record){
15338         return this.data.indexOf(record);
15339     },
15340
15341     /**
15342      * Get the index within the cache of the Record with the passed id.
15343      * @param {String} id The id of the Record to find.
15344      * @return {Number} The index of the Record. Returns -1 if not found.
15345      */
15346     indexOfId : function(id){
15347         return this.data.indexOfKey(id);
15348     },
15349
15350     /**
15351      * Get the Record with the specified id.
15352      * @param {String} id The id of the Record to find.
15353      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15354      */
15355     getById : function(id){
15356         return this.data.key(id);
15357     },
15358
15359     /**
15360      * Get the Record at the specified index.
15361      * @param {Number} index The index of the Record to find.
15362      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15363      */
15364     getAt : function(index){
15365         return this.data.itemAt(index);
15366     },
15367
15368     /**
15369      * Returns a range of Records between specified indices.
15370      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15371      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15372      * @return {Roo.data.Record[]} An array of Records
15373      */
15374     getRange : function(start, end){
15375         return this.data.getRange(start, end);
15376     },
15377
15378     // private
15379     storeOptions : function(o){
15380         o = Roo.apply({}, o);
15381         delete o.callback;
15382         delete o.scope;
15383         this.lastOptions = o;
15384     },
15385
15386     /**
15387      * Loads the Record cache from the configured Proxy using the configured Reader.
15388      * <p>
15389      * If using remote paging, then the first load call must specify the <em>start</em>
15390      * and <em>limit</em> properties in the options.params property to establish the initial
15391      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15392      * <p>
15393      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15394      * and this call will return before the new data has been loaded. Perform any post-processing
15395      * in a callback function, or in a "load" event handler.</strong>
15396      * <p>
15397      * @param {Object} options An object containing properties which control loading options:<ul>
15398      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15399      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15400      * <pre>
15401                 {
15402                     data : data,  // array of key=>value data like JsonReader
15403                     total : data.length,
15404                     success : true
15405                     
15406                 }
15407         </pre>
15408             }.</li>
15409      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15410      * passed the following arguments:<ul>
15411      * <li>r : Roo.data.Record[]</li>
15412      * <li>options: Options object from the load call</li>
15413      * <li>success: Boolean success indicator</li></ul></li>
15414      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15415      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15416      * </ul>
15417      */
15418     load : function(options){
15419         options = options || {};
15420         if(this.fireEvent("beforeload", this, options) !== false){
15421             this.storeOptions(options);
15422             var p = Roo.apply(options.params || {}, this.baseParams);
15423             // if meta was not loaded from remote source.. try requesting it.
15424             if (!this.reader.metaFromRemote) {
15425                 p._requestMeta = 1;
15426             }
15427             if(this.sortInfo && this.remoteSort){
15428                 var pn = this.paramNames;
15429                 p[pn["sort"]] = this.sortInfo.field;
15430                 p[pn["dir"]] = this.sortInfo.direction;
15431             }
15432             if (this.multiSort) {
15433                 var pn = this.paramNames;
15434                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15435             }
15436             
15437             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15438         }
15439     },
15440
15441     /**
15442      * Reloads the Record cache from the configured Proxy using the configured Reader and
15443      * the options from the last load operation performed.
15444      * @param {Object} options (optional) An object containing properties which may override the options
15445      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15446      * the most recently used options are reused).
15447      */
15448     reload : function(options){
15449         this.load(Roo.applyIf(options||{}, this.lastOptions));
15450     },
15451
15452     // private
15453     // Called as a callback by the Reader during a load operation.
15454     loadRecords : function(o, options, success){
15455          
15456         if(!o){
15457             if(success !== false){
15458                 this.fireEvent("load", this, [], options, o);
15459             }
15460             if(options.callback){
15461                 options.callback.call(options.scope || this, [], options, false);
15462             }
15463             return;
15464         }
15465         // if data returned failure - throw an exception.
15466         if (o.success === false) {
15467             // show a message if no listener is registered.
15468             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15469                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15470             }
15471             // loadmask wil be hooked into this..
15472             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15473             return;
15474         }
15475         var r = o.records, t = o.totalRecords || r.length;
15476         
15477         this.fireEvent("beforeloadadd", this, r, options, o);
15478         
15479         if(!options || options.add !== true){
15480             if(this.pruneModifiedRecords){
15481                 this.modified = [];
15482             }
15483             for(var i = 0, len = r.length; i < len; i++){
15484                 r[i].join(this);
15485             }
15486             if(this.snapshot){
15487                 this.data = this.snapshot;
15488                 delete this.snapshot;
15489             }
15490             this.data.clear();
15491             this.data.addAll(r);
15492             this.totalLength = t;
15493             this.applySort();
15494             this.fireEvent("datachanged", this);
15495         }else{
15496             this.totalLength = Math.max(t, this.data.length+r.length);
15497             this.add(r);
15498         }
15499         
15500         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15501                 
15502             var e = new Roo.data.Record({});
15503
15504             e.set(this.parent.displayField, this.parent.emptyTitle);
15505             e.set(this.parent.valueField, '');
15506
15507             this.insert(0, e);
15508         }
15509             
15510         this.fireEvent("load", this, r, options, o);
15511         if(options.callback){
15512             options.callback.call(options.scope || this, r, options, true);
15513         }
15514     },
15515
15516
15517     /**
15518      * Loads data from a passed data block. A Reader which understands the format of the data
15519      * must have been configured in the constructor.
15520      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15521      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15522      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15523      */
15524     loadData : function(o, append){
15525         var r = this.reader.readRecords(o);
15526         this.loadRecords(r, {add: append}, true);
15527     },
15528     
15529      /**
15530      * using 'cn' the nested child reader read the child array into it's child stores.
15531      * @param {Object} rec The record with a 'children array
15532      */
15533     loadDataFromChildren : function(rec)
15534     {
15535         this.loadData(this.reader.toLoadData(rec));
15536     },
15537     
15538
15539     /**
15540      * Gets the number of cached records.
15541      * <p>
15542      * <em>If using paging, this may not be the total size of the dataset. If the data object
15543      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15544      * the data set size</em>
15545      */
15546     getCount : function(){
15547         return this.data.length || 0;
15548     },
15549
15550     /**
15551      * Gets the total number of records in the dataset as returned by the server.
15552      * <p>
15553      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15554      * the dataset size</em>
15555      */
15556     getTotalCount : function(){
15557         return this.totalLength || 0;
15558     },
15559
15560     /**
15561      * Returns the sort state of the Store as an object with two properties:
15562      * <pre><code>
15563  field {String} The name of the field by which the Records are sorted
15564  direction {String} The sort order, "ASC" or "DESC"
15565      * </code></pre>
15566      */
15567     getSortState : function(){
15568         return this.sortInfo;
15569     },
15570
15571     // private
15572     applySort : function(){
15573         if(this.sortInfo && !this.remoteSort){
15574             var s = this.sortInfo, f = s.field;
15575             var st = this.fields.get(f).sortType;
15576             var fn = function(r1, r2){
15577                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15578                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15579             };
15580             this.data.sort(s.direction, fn);
15581             if(this.snapshot && this.snapshot != this.data){
15582                 this.snapshot.sort(s.direction, fn);
15583             }
15584         }
15585     },
15586
15587     /**
15588      * Sets the default sort column and order to be used by the next load operation.
15589      * @param {String} fieldName The name of the field to sort by.
15590      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15591      */
15592     setDefaultSort : function(field, dir){
15593         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15594     },
15595
15596     /**
15597      * Sort the Records.
15598      * If remote sorting is used, the sort is performed on the server, and the cache is
15599      * reloaded. If local sorting is used, the cache is sorted internally.
15600      * @param {String} fieldName The name of the field to sort by.
15601      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15602      */
15603     sort : function(fieldName, dir){
15604         var f = this.fields.get(fieldName);
15605         if(!dir){
15606             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15607             
15608             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15609                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15610             }else{
15611                 dir = f.sortDir;
15612             }
15613         }
15614         this.sortToggle[f.name] = dir;
15615         this.sortInfo = {field: f.name, direction: dir};
15616         if(!this.remoteSort){
15617             this.applySort();
15618             this.fireEvent("datachanged", this);
15619         }else{
15620             this.load(this.lastOptions);
15621         }
15622     },
15623
15624     /**
15625      * Calls the specified function for each of the Records in the cache.
15626      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15627      * Returning <em>false</em> aborts and exits the iteration.
15628      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15629      */
15630     each : function(fn, scope){
15631         this.data.each(fn, scope);
15632     },
15633
15634     /**
15635      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15636      * (e.g., during paging).
15637      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15638      */
15639     getModifiedRecords : function(){
15640         return this.modified;
15641     },
15642
15643     // private
15644     createFilterFn : function(property, value, anyMatch){
15645         if(!value.exec){ // not a regex
15646             value = String(value);
15647             if(value.length == 0){
15648                 return false;
15649             }
15650             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15651         }
15652         return function(r){
15653             return value.test(r.data[property]);
15654         };
15655     },
15656
15657     /**
15658      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15659      * @param {String} property A field on your records
15660      * @param {Number} start The record index to start at (defaults to 0)
15661      * @param {Number} end The last record index to include (defaults to length - 1)
15662      * @return {Number} The sum
15663      */
15664     sum : function(property, start, end){
15665         var rs = this.data.items, v = 0;
15666         start = start || 0;
15667         end = (end || end === 0) ? end : rs.length-1;
15668
15669         for(var i = start; i <= end; i++){
15670             v += (rs[i].data[property] || 0);
15671         }
15672         return v;
15673     },
15674
15675     /**
15676      * Filter the records by a specified property.
15677      * @param {String} field A field on your records
15678      * @param {String/RegExp} value Either a string that the field
15679      * should start with or a RegExp to test against the field
15680      * @param {Boolean} anyMatch True to match any part not just the beginning
15681      */
15682     filter : function(property, value, anyMatch){
15683         var fn = this.createFilterFn(property, value, anyMatch);
15684         return fn ? this.filterBy(fn) : this.clearFilter();
15685     },
15686
15687     /**
15688      * Filter by a function. The specified function will be called with each
15689      * record in this data source. If the function returns true the record is included,
15690      * otherwise it is filtered.
15691      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15692      * @param {Object} scope (optional) The scope of the function (defaults to this)
15693      */
15694     filterBy : function(fn, scope){
15695         this.snapshot = this.snapshot || this.data;
15696         this.data = this.queryBy(fn, scope||this);
15697         this.fireEvent("datachanged", this);
15698     },
15699
15700     /**
15701      * Query the records by a specified property.
15702      * @param {String} field A field on your records
15703      * @param {String/RegExp} value Either a string that the field
15704      * should start with or a RegExp to test against the field
15705      * @param {Boolean} anyMatch True to match any part not just the beginning
15706      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15707      */
15708     query : function(property, value, anyMatch){
15709         var fn = this.createFilterFn(property, value, anyMatch);
15710         return fn ? this.queryBy(fn) : this.data.clone();
15711     },
15712
15713     /**
15714      * Query by a function. The specified function will be called with each
15715      * record in this data source. If the function returns true the record is included
15716      * in the results.
15717      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15718      * @param {Object} scope (optional) The scope of the function (defaults to this)
15719       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15720      **/
15721     queryBy : function(fn, scope){
15722         var data = this.snapshot || this.data;
15723         return data.filterBy(fn, scope||this);
15724     },
15725
15726     /**
15727      * Collects unique values for a particular dataIndex from this store.
15728      * @param {String} dataIndex The property to collect
15729      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15730      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15731      * @return {Array} An array of the unique values
15732      **/
15733     collect : function(dataIndex, allowNull, bypassFilter){
15734         var d = (bypassFilter === true && this.snapshot) ?
15735                 this.snapshot.items : this.data.items;
15736         var v, sv, r = [], l = {};
15737         for(var i = 0, len = d.length; i < len; i++){
15738             v = d[i].data[dataIndex];
15739             sv = String(v);
15740             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15741                 l[sv] = true;
15742                 r[r.length] = v;
15743             }
15744         }
15745         return r;
15746     },
15747
15748     /**
15749      * Revert to a view of the Record cache with no filtering applied.
15750      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15751      */
15752     clearFilter : function(suppressEvent){
15753         if(this.snapshot && this.snapshot != this.data){
15754             this.data = this.snapshot;
15755             delete this.snapshot;
15756             if(suppressEvent !== true){
15757                 this.fireEvent("datachanged", this);
15758             }
15759         }
15760     },
15761
15762     // private
15763     afterEdit : function(record){
15764         if(this.modified.indexOf(record) == -1){
15765             this.modified.push(record);
15766         }
15767         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15768     },
15769     
15770     // private
15771     afterReject : function(record){
15772         this.modified.remove(record);
15773         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15774     },
15775
15776     // private
15777     afterCommit : function(record){
15778         this.modified.remove(record);
15779         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15780     },
15781
15782     /**
15783      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15784      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15785      */
15786     commitChanges : function(){
15787         var m = this.modified.slice(0);
15788         this.modified = [];
15789         for(var i = 0, len = m.length; i < len; i++){
15790             m[i].commit();
15791         }
15792     },
15793
15794     /**
15795      * Cancel outstanding changes on all changed records.
15796      */
15797     rejectChanges : function(){
15798         var m = this.modified.slice(0);
15799         this.modified = [];
15800         for(var i = 0, len = m.length; i < len; i++){
15801             m[i].reject();
15802         }
15803     },
15804
15805     onMetaChange : function(meta, rtype, o){
15806         this.recordType = rtype;
15807         this.fields = rtype.prototype.fields;
15808         delete this.snapshot;
15809         this.sortInfo = meta.sortInfo || this.sortInfo;
15810         this.modified = [];
15811         this.fireEvent('metachange', this, this.reader.meta);
15812     },
15813     
15814     moveIndex : function(data, type)
15815     {
15816         var index = this.indexOf(data);
15817         
15818         var newIndex = index + type;
15819         
15820         this.remove(data);
15821         
15822         this.insert(newIndex, data);
15823         
15824     }
15825 });/*
15826  * Based on:
15827  * Ext JS Library 1.1.1
15828  * Copyright(c) 2006-2007, Ext JS, LLC.
15829  *
15830  * Originally Released Under LGPL - original licence link has changed is not relivant.
15831  *
15832  * Fork - LGPL
15833  * <script type="text/javascript">
15834  */
15835
15836 /**
15837  * @class Roo.data.SimpleStore
15838  * @extends Roo.data.Store
15839  * Small helper class to make creating Stores from Array data easier.
15840  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15841  * @cfg {Array} fields An array of field definition objects, or field name strings.
15842  * @cfg {Object} an existing reader (eg. copied from another store)
15843  * @cfg {Array} data The multi-dimensional array of data
15844  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15845  * @cfg {Roo.data.Reader} reader  [not-required] 
15846  * @constructor
15847  * @param {Object} config
15848  */
15849 Roo.data.SimpleStore = function(config)
15850 {
15851     Roo.data.SimpleStore.superclass.constructor.call(this, {
15852         isLocal : true,
15853         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15854                 id: config.id
15855             },
15856             Roo.data.Record.create(config.fields)
15857         ),
15858         proxy : new Roo.data.MemoryProxy(config.data)
15859     });
15860     this.load();
15861 };
15862 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15863  * Based on:
15864  * Ext JS Library 1.1.1
15865  * Copyright(c) 2006-2007, Ext JS, LLC.
15866  *
15867  * Originally Released Under LGPL - original licence link has changed is not relivant.
15868  *
15869  * Fork - LGPL
15870  * <script type="text/javascript">
15871  */
15872
15873 /**
15874 /**
15875  * @extends Roo.data.Store
15876  * @class Roo.data.JsonStore
15877  * Small helper class to make creating Stores for JSON data easier. <br/>
15878 <pre><code>
15879 var store = new Roo.data.JsonStore({
15880     url: 'get-images.php',
15881     root: 'images',
15882     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15883 });
15884 </code></pre>
15885  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15886  * JsonReader and HttpProxy (unless inline data is provided).</b>
15887  * @cfg {Array} fields An array of field definition objects, or field name strings.
15888  * @constructor
15889  * @param {Object} config
15890  */
15891 Roo.data.JsonStore = function(c){
15892     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15893         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15894         reader: new Roo.data.JsonReader(c, c.fields)
15895     }));
15896 };
15897 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15898  * Based on:
15899  * Ext JS Library 1.1.1
15900  * Copyright(c) 2006-2007, Ext JS, LLC.
15901  *
15902  * Originally Released Under LGPL - original licence link has changed is not relivant.
15903  *
15904  * Fork - LGPL
15905  * <script type="text/javascript">
15906  */
15907
15908  
15909 Roo.data.Field = function(config){
15910     if(typeof config == "string"){
15911         config = {name: config};
15912     }
15913     Roo.apply(this, config);
15914     
15915     if(!this.type){
15916         this.type = "auto";
15917     }
15918     
15919     var st = Roo.data.SortTypes;
15920     // named sortTypes are supported, here we look them up
15921     if(typeof this.sortType == "string"){
15922         this.sortType = st[this.sortType];
15923     }
15924     
15925     // set default sortType for strings and dates
15926     if(!this.sortType){
15927         switch(this.type){
15928             case "string":
15929                 this.sortType = st.asUCString;
15930                 break;
15931             case "date":
15932                 this.sortType = st.asDate;
15933                 break;
15934             default:
15935                 this.sortType = st.none;
15936         }
15937     }
15938
15939     // define once
15940     var stripRe = /[\$,%]/g;
15941
15942     // prebuilt conversion function for this field, instead of
15943     // switching every time we're reading a value
15944     if(!this.convert){
15945         var cv, dateFormat = this.dateFormat;
15946         switch(this.type){
15947             case "":
15948             case "auto":
15949             case undefined:
15950                 cv = function(v){ return v; };
15951                 break;
15952             case "string":
15953                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15954                 break;
15955             case "int":
15956                 cv = function(v){
15957                     return v !== undefined && v !== null && v !== '' ?
15958                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15959                     };
15960                 break;
15961             case "float":
15962                 cv = function(v){
15963                     return v !== undefined && v !== null && v !== '' ?
15964                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15965                     };
15966                 break;
15967             case "bool":
15968             case "boolean":
15969                 cv = function(v){ return v === true || v === "true" || v == 1; };
15970                 break;
15971             case "date":
15972                 cv = function(v){
15973                     if(!v){
15974                         return '';
15975                     }
15976                     if(v instanceof Date){
15977                         return v;
15978                     }
15979                     if(dateFormat){
15980                         if(dateFormat == "timestamp"){
15981                             return new Date(v*1000);
15982                         }
15983                         return Date.parseDate(v, dateFormat);
15984                     }
15985                     var parsed = Date.parse(v);
15986                     return parsed ? new Date(parsed) : null;
15987                 };
15988              break;
15989             
15990         }
15991         this.convert = cv;
15992     }
15993 };
15994
15995 Roo.data.Field.prototype = {
15996     dateFormat: null,
15997     defaultValue: "",
15998     mapping: null,
15999     sortType : null,
16000     sortDir : "ASC"
16001 };/*
16002  * Based on:
16003  * Ext JS Library 1.1.1
16004  * Copyright(c) 2006-2007, Ext JS, LLC.
16005  *
16006  * Originally Released Under LGPL - original licence link has changed is not relivant.
16007  *
16008  * Fork - LGPL
16009  * <script type="text/javascript">
16010  */
16011  
16012 // Base class for reading structured data from a data source.  This class is intended to be
16013 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16014
16015 /**
16016  * @class Roo.data.DataReader
16017  * @abstract
16018  * Base class for reading structured data from a data source.  This class is intended to be
16019  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16020  */
16021
16022 Roo.data.DataReader = function(meta, recordType){
16023     
16024     this.meta = meta;
16025     
16026     this.recordType = recordType instanceof Array ? 
16027         Roo.data.Record.create(recordType) : recordType;
16028 };
16029
16030 Roo.data.DataReader.prototype = {
16031     
16032     
16033     readerType : 'Data',
16034      /**
16035      * Create an empty record
16036      * @param {Object} data (optional) - overlay some values
16037      * @return {Roo.data.Record} record created.
16038      */
16039     newRow :  function(d) {
16040         var da =  {};
16041         this.recordType.prototype.fields.each(function(c) {
16042             switch( c.type) {
16043                 case 'int' : da[c.name] = 0; break;
16044                 case 'date' : da[c.name] = new Date(); break;
16045                 case 'float' : da[c.name] = 0.0; break;
16046                 case 'boolean' : da[c.name] = false; break;
16047                 default : da[c.name] = ""; break;
16048             }
16049             
16050         });
16051         return new this.recordType(Roo.apply(da, d));
16052     }
16053     
16054     
16055 };/*
16056  * Based on:
16057  * Ext JS Library 1.1.1
16058  * Copyright(c) 2006-2007, Ext JS, LLC.
16059  *
16060  * Originally Released Under LGPL - original licence link has changed is not relivant.
16061  *
16062  * Fork - LGPL
16063  * <script type="text/javascript">
16064  */
16065
16066 /**
16067  * @class Roo.data.DataProxy
16068  * @extends Roo.util.Observable
16069  * @abstract
16070  * This class is an abstract base class for implementations which provide retrieval of
16071  * unformatted data objects.<br>
16072  * <p>
16073  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16074  * (of the appropriate type which knows how to parse the data object) to provide a block of
16075  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16076  * <p>
16077  * Custom implementations must implement the load method as described in
16078  * {@link Roo.data.HttpProxy#load}.
16079  */
16080 Roo.data.DataProxy = function(){
16081     this.addEvents({
16082         /**
16083          * @event beforeload
16084          * Fires before a network request is made to retrieve a data object.
16085          * @param {Object} This DataProxy object.
16086          * @param {Object} params The params parameter to the load function.
16087          */
16088         beforeload : true,
16089         /**
16090          * @event load
16091          * Fires before the load method's callback is called.
16092          * @param {Object} This DataProxy object.
16093          * @param {Object} o The data object.
16094          * @param {Object} arg The callback argument object passed to the load function.
16095          */
16096         load : true,
16097         /**
16098          * @event loadexception
16099          * Fires if an Exception occurs during data retrieval.
16100          * @param {Object} This DataProxy object.
16101          * @param {Object} o The data object.
16102          * @param {Object} arg The callback argument object passed to the load function.
16103          * @param {Object} e The Exception.
16104          */
16105         loadexception : true
16106     });
16107     Roo.data.DataProxy.superclass.constructor.call(this);
16108 };
16109
16110 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16111
16112     /**
16113      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16114      */
16115 /*
16116  * Based on:
16117  * Ext JS Library 1.1.1
16118  * Copyright(c) 2006-2007, Ext JS, LLC.
16119  *
16120  * Originally Released Under LGPL - original licence link has changed is not relivant.
16121  *
16122  * Fork - LGPL
16123  * <script type="text/javascript">
16124  */
16125 /**
16126  * @class Roo.data.MemoryProxy
16127  * @extends Roo.data.DataProxy
16128  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16129  * to the Reader when its load method is called.
16130  * @constructor
16131  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16132  */
16133 Roo.data.MemoryProxy = function(config){
16134     var data = config;
16135     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16136         data = config.data;
16137     }
16138     Roo.data.MemoryProxy.superclass.constructor.call(this);
16139     this.data = data;
16140 };
16141
16142 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16143     
16144     /**
16145      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16146      */
16147     /**
16148      * Load data from the requested source (in this case an in-memory
16149      * data object passed to the constructor), read the data object into
16150      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16151      * process that block using the passed callback.
16152      * @param {Object} params This parameter is not used by the MemoryProxy class.
16153      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16154      * object into a block of Roo.data.Records.
16155      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16156      * The function must be passed <ul>
16157      * <li>The Record block object</li>
16158      * <li>The "arg" argument from the load function</li>
16159      * <li>A boolean success indicator</li>
16160      * </ul>
16161      * @param {Object} scope The scope in which to call the callback
16162      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16163      */
16164     load : function(params, reader, callback, scope, arg){
16165         params = params || {};
16166         var result;
16167         try {
16168             result = reader.readRecords(params.data ? params.data :this.data);
16169         }catch(e){
16170             this.fireEvent("loadexception", this, arg, null, e);
16171             callback.call(scope, null, arg, false);
16172             return;
16173         }
16174         callback.call(scope, result, arg, true);
16175     },
16176     
16177     // private
16178     update : function(params, records){
16179         
16180     }
16181 });/*
16182  * Based on:
16183  * Ext JS Library 1.1.1
16184  * Copyright(c) 2006-2007, Ext JS, LLC.
16185  *
16186  * Originally Released Under LGPL - original licence link has changed is not relivant.
16187  *
16188  * Fork - LGPL
16189  * <script type="text/javascript">
16190  */
16191 /**
16192  * @class Roo.data.HttpProxy
16193  * @extends Roo.data.DataProxy
16194  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16195  * configured to reference a certain URL.<br><br>
16196  * <p>
16197  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16198  * from which the running page was served.<br><br>
16199  * <p>
16200  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16201  * <p>
16202  * Be aware that to enable the browser to parse an XML document, the server must set
16203  * the Content-Type header in the HTTP response to "text/xml".
16204  * @constructor
16205  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16206  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16207  * will be used to make the request.
16208  */
16209 Roo.data.HttpProxy = function(conn){
16210     Roo.data.HttpProxy.superclass.constructor.call(this);
16211     // is conn a conn config or a real conn?
16212     this.conn = conn;
16213     this.useAjax = !conn || !conn.events;
16214   
16215 };
16216
16217 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16218     // thse are take from connection...
16219     
16220     /**
16221      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16222      */
16223     /**
16224      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16225      * extra parameters to each request made by this object. (defaults to undefined)
16226      */
16227     /**
16228      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16229      *  to each request made by this object. (defaults to undefined)
16230      */
16231     /**
16232      * @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)
16233      */
16234     /**
16235      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16236      */
16237      /**
16238      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16239      * @type Boolean
16240      */
16241   
16242
16243     /**
16244      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16245      * @type Boolean
16246      */
16247     /**
16248      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16249      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16250      * a finer-grained basis than the DataProxy events.
16251      */
16252     getConnection : function(){
16253         return this.useAjax ? Roo.Ajax : this.conn;
16254     },
16255
16256     /**
16257      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16258      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16259      * process that block using the passed callback.
16260      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16261      * for the request to the remote server.
16262      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16263      * object into a block of Roo.data.Records.
16264      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16265      * The function must be passed <ul>
16266      * <li>The Record block object</li>
16267      * <li>The "arg" argument from the load function</li>
16268      * <li>A boolean success indicator</li>
16269      * </ul>
16270      * @param {Object} scope The scope in which to call the callback
16271      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16272      */
16273     load : function(params, reader, callback, scope, arg){
16274         if(this.fireEvent("beforeload", this, params) !== false){
16275             var  o = {
16276                 params : params || {},
16277                 request: {
16278                     callback : callback,
16279                     scope : scope,
16280                     arg : arg
16281                 },
16282                 reader: reader,
16283                 callback : this.loadResponse,
16284                 scope: this
16285             };
16286             if(this.useAjax){
16287                 Roo.applyIf(o, this.conn);
16288                 if(this.activeRequest){
16289                     Roo.Ajax.abort(this.activeRequest);
16290                 }
16291                 this.activeRequest = Roo.Ajax.request(o);
16292             }else{
16293                 this.conn.request(o);
16294             }
16295         }else{
16296             callback.call(scope||this, null, arg, false);
16297         }
16298     },
16299
16300     // private
16301     loadResponse : function(o, success, response){
16302         delete this.activeRequest;
16303         if(!success){
16304             this.fireEvent("loadexception", this, o, response);
16305             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16306             return;
16307         }
16308         var result;
16309         try {
16310             result = o.reader.read(response);
16311         }catch(e){
16312             o.success = false;
16313             o.raw = { errorMsg : response.responseText };
16314             this.fireEvent("loadexception", this, o, response, e);
16315             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16316             return;
16317         }
16318         
16319         this.fireEvent("load", this, o, o.request.arg);
16320         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16321     },
16322
16323     // private
16324     update : function(dataSet){
16325
16326     },
16327
16328     // private
16329     updateResponse : function(dataSet){
16330
16331     }
16332 });/*
16333  * Based on:
16334  * Ext JS Library 1.1.1
16335  * Copyright(c) 2006-2007, Ext JS, LLC.
16336  *
16337  * Originally Released Under LGPL - original licence link has changed is not relivant.
16338  *
16339  * Fork - LGPL
16340  * <script type="text/javascript">
16341  */
16342
16343 /**
16344  * @class Roo.data.ScriptTagProxy
16345  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16346  * other than the originating domain of the running page.<br><br>
16347  * <p>
16348  * <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
16349  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16350  * <p>
16351  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16352  * source code that is used as the source inside a &lt;script> tag.<br><br>
16353  * <p>
16354  * In order for the browser to process the returned data, the server must wrap the data object
16355  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16356  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16357  * depending on whether the callback name was passed:
16358  * <p>
16359  * <pre><code>
16360 boolean scriptTag = false;
16361 String cb = request.getParameter("callback");
16362 if (cb != null) {
16363     scriptTag = true;
16364     response.setContentType("text/javascript");
16365 } else {
16366     response.setContentType("application/x-json");
16367 }
16368 Writer out = response.getWriter();
16369 if (scriptTag) {
16370     out.write(cb + "(");
16371 }
16372 out.print(dataBlock.toJsonString());
16373 if (scriptTag) {
16374     out.write(");");
16375 }
16376 </pre></code>
16377  *
16378  * @constructor
16379  * @param {Object} config A configuration object.
16380  */
16381 Roo.data.ScriptTagProxy = function(config){
16382     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16383     Roo.apply(this, config);
16384     this.head = document.getElementsByTagName("head")[0];
16385 };
16386
16387 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16388
16389 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16390     /**
16391      * @cfg {String} url The URL from which to request the data object.
16392      */
16393     /**
16394      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16395      */
16396     timeout : 30000,
16397     /**
16398      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16399      * the server the name of the callback function set up by the load call to process the returned data object.
16400      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16401      * javascript output which calls this named function passing the data object as its only parameter.
16402      */
16403     callbackParam : "callback",
16404     /**
16405      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16406      * name to the request.
16407      */
16408     nocache : true,
16409
16410     /**
16411      * Load data from the configured URL, read the data object into
16412      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16413      * process that block using the passed callback.
16414      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16415      * for the request to the remote server.
16416      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16417      * object into a block of Roo.data.Records.
16418      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16419      * The function must be passed <ul>
16420      * <li>The Record block object</li>
16421      * <li>The "arg" argument from the load function</li>
16422      * <li>A boolean success indicator</li>
16423      * </ul>
16424      * @param {Object} scope The scope in which to call the callback
16425      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16426      */
16427     load : function(params, reader, callback, scope, arg){
16428         if(this.fireEvent("beforeload", this, params) !== false){
16429
16430             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16431
16432             var url = this.url;
16433             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16434             if(this.nocache){
16435                 url += "&_dc=" + (new Date().getTime());
16436             }
16437             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16438             var trans = {
16439                 id : transId,
16440                 cb : "stcCallback"+transId,
16441                 scriptId : "stcScript"+transId,
16442                 params : params,
16443                 arg : arg,
16444                 url : url,
16445                 callback : callback,
16446                 scope : scope,
16447                 reader : reader
16448             };
16449             var conn = this;
16450
16451             window[trans.cb] = function(o){
16452                 conn.handleResponse(o, trans);
16453             };
16454
16455             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16456
16457             if(this.autoAbort !== false){
16458                 this.abort();
16459             }
16460
16461             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16462
16463             var script = document.createElement("script");
16464             script.setAttribute("src", url);
16465             script.setAttribute("type", "text/javascript");
16466             script.setAttribute("id", trans.scriptId);
16467             this.head.appendChild(script);
16468
16469             this.trans = trans;
16470         }else{
16471             callback.call(scope||this, null, arg, false);
16472         }
16473     },
16474
16475     // private
16476     isLoading : function(){
16477         return this.trans ? true : false;
16478     },
16479
16480     /**
16481      * Abort the current server request.
16482      */
16483     abort : function(){
16484         if(this.isLoading()){
16485             this.destroyTrans(this.trans);
16486         }
16487     },
16488
16489     // private
16490     destroyTrans : function(trans, isLoaded){
16491         this.head.removeChild(document.getElementById(trans.scriptId));
16492         clearTimeout(trans.timeoutId);
16493         if(isLoaded){
16494             window[trans.cb] = undefined;
16495             try{
16496                 delete window[trans.cb];
16497             }catch(e){}
16498         }else{
16499             // if hasn't been loaded, wait for load to remove it to prevent script error
16500             window[trans.cb] = function(){
16501                 window[trans.cb] = undefined;
16502                 try{
16503                     delete window[trans.cb];
16504                 }catch(e){}
16505             };
16506         }
16507     },
16508
16509     // private
16510     handleResponse : function(o, trans){
16511         this.trans = false;
16512         this.destroyTrans(trans, true);
16513         var result;
16514         try {
16515             result = trans.reader.readRecords(o);
16516         }catch(e){
16517             this.fireEvent("loadexception", this, o, trans.arg, e);
16518             trans.callback.call(trans.scope||window, null, trans.arg, false);
16519             return;
16520         }
16521         this.fireEvent("load", this, o, trans.arg);
16522         trans.callback.call(trans.scope||window, result, trans.arg, true);
16523     },
16524
16525     // private
16526     handleFailure : function(trans){
16527         this.trans = false;
16528         this.destroyTrans(trans, false);
16529         this.fireEvent("loadexception", this, null, trans.arg);
16530         trans.callback.call(trans.scope||window, null, trans.arg, false);
16531     }
16532 });/*
16533  * Based on:
16534  * Ext JS Library 1.1.1
16535  * Copyright(c) 2006-2007, Ext JS, LLC.
16536  *
16537  * Originally Released Under LGPL - original licence link has changed is not relivant.
16538  *
16539  * Fork - LGPL
16540  * <script type="text/javascript">
16541  */
16542
16543 /**
16544  * @class Roo.data.JsonReader
16545  * @extends Roo.data.DataReader
16546  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16547  * based on mappings in a provided Roo.data.Record constructor.
16548  * 
16549  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16550  * in the reply previously. 
16551  * 
16552  * <p>
16553  * Example code:
16554  * <pre><code>
16555 var RecordDef = Roo.data.Record.create([
16556     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16557     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16558 ]);
16559 var myReader = new Roo.data.JsonReader({
16560     totalProperty: "results",    // The property which contains the total dataset size (optional)
16561     root: "rows",                // The property which contains an Array of row objects
16562     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16563 }, RecordDef);
16564 </code></pre>
16565  * <p>
16566  * This would consume a JSON file like this:
16567  * <pre><code>
16568 { 'results': 2, 'rows': [
16569     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16570     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16571 }
16572 </code></pre>
16573  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16574  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16575  * paged from the remote server.
16576  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16577  * @cfg {String} root name of the property which contains the Array of row objects.
16578  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16579  * @cfg {Array} fields Array of field definition objects
16580  * @constructor
16581  * Create a new JsonReader
16582  * @param {Object} meta Metadata configuration options
16583  * @param {Object} recordType Either an Array of field definition objects,
16584  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16585  */
16586 Roo.data.JsonReader = function(meta, recordType){
16587     
16588     meta = meta || {};
16589     // set some defaults:
16590     Roo.applyIf(meta, {
16591         totalProperty: 'total',
16592         successProperty : 'success',
16593         root : 'data',
16594         id : 'id'
16595     });
16596     
16597     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16598 };
16599 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16600     
16601     readerType : 'Json',
16602     
16603     /**
16604      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16605      * Used by Store query builder to append _requestMeta to params.
16606      * 
16607      */
16608     metaFromRemote : false,
16609     /**
16610      * This method is only used by a DataProxy which has retrieved data from a remote server.
16611      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16612      * @return {Object} data A data block which is used by an Roo.data.Store object as
16613      * a cache of Roo.data.Records.
16614      */
16615     read : function(response){
16616         var json = response.responseText;
16617        
16618         var o = /* eval:var:o */ eval("("+json+")");
16619         if(!o) {
16620             throw {message: "JsonReader.read: Json object not found"};
16621         }
16622         
16623         if(o.metaData){
16624             
16625             delete this.ef;
16626             this.metaFromRemote = true;
16627             this.meta = o.metaData;
16628             this.recordType = Roo.data.Record.create(o.metaData.fields);
16629             this.onMetaChange(this.meta, this.recordType, o);
16630         }
16631         return this.readRecords(o);
16632     },
16633
16634     // private function a store will implement
16635     onMetaChange : function(meta, recordType, o){
16636
16637     },
16638
16639     /**
16640          * @ignore
16641          */
16642     simpleAccess: function(obj, subsc) {
16643         return obj[subsc];
16644     },
16645
16646         /**
16647          * @ignore
16648          */
16649     getJsonAccessor: function(){
16650         var re = /[\[\.]/;
16651         return function(expr) {
16652             try {
16653                 return(re.test(expr))
16654                     ? new Function("obj", "return obj." + expr)
16655                     : function(obj){
16656                         return obj[expr];
16657                     };
16658             } catch(e){}
16659             return Roo.emptyFn;
16660         };
16661     }(),
16662
16663     /**
16664      * Create a data block containing Roo.data.Records from an XML document.
16665      * @param {Object} o An object which contains an Array of row objects in the property specified
16666      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16667      * which contains the total size of the dataset.
16668      * @return {Object} data A data block which is used by an Roo.data.Store object as
16669      * a cache of Roo.data.Records.
16670      */
16671     readRecords : function(o){
16672         /**
16673          * After any data loads, the raw JSON data is available for further custom processing.
16674          * @type Object
16675          */
16676         this.o = o;
16677         var s = this.meta, Record = this.recordType,
16678             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16679
16680 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16681         if (!this.ef) {
16682             if(s.totalProperty) {
16683                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16684                 }
16685                 if(s.successProperty) {
16686                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16687                 }
16688                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16689                 if (s.id) {
16690                         var g = this.getJsonAccessor(s.id);
16691                         this.getId = function(rec) {
16692                                 var r = g(rec);  
16693                                 return (r === undefined || r === "") ? null : r;
16694                         };
16695                 } else {
16696                         this.getId = function(){return null;};
16697                 }
16698             this.ef = [];
16699             for(var jj = 0; jj < fl; jj++){
16700                 f = fi[jj];
16701                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16702                 this.ef[jj] = this.getJsonAccessor(map);
16703             }
16704         }
16705
16706         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16707         if(s.totalProperty){
16708             var vt = parseInt(this.getTotal(o), 10);
16709             if(!isNaN(vt)){
16710                 totalRecords = vt;
16711             }
16712         }
16713         if(s.successProperty){
16714             var vs = this.getSuccess(o);
16715             if(vs === false || vs === 'false'){
16716                 success = false;
16717             }
16718         }
16719         var records = [];
16720         for(var i = 0; i < c; i++){
16721             var n = root[i];
16722             var values = {};
16723             var id = this.getId(n);
16724             for(var j = 0; j < fl; j++){
16725                 f = fi[j];
16726                                 var v = this.ef[j](n);
16727                                 if (!f.convert) {
16728                                         Roo.log('missing convert for ' + f.name);
16729                                         Roo.log(f);
16730                                         continue;
16731                                 }
16732                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16733             }
16734                         if (!Record) {
16735                                 return {
16736                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16737                                         success : false,
16738                                         records : [],
16739                                         totalRecords : 0
16740                                 };
16741                         }
16742             var record = new Record(values, id);
16743             record.json = n;
16744             records[i] = record;
16745         }
16746         return {
16747             raw : o,
16748             success : success,
16749             records : records,
16750             totalRecords : totalRecords
16751         };
16752     },
16753     // used when loading children.. @see loadDataFromChildren
16754     toLoadData: function(rec)
16755     {
16756         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16757         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16758         return { data : data, total : data.length };
16759         
16760     }
16761 });/*
16762  * Based on:
16763  * Ext JS Library 1.1.1
16764  * Copyright(c) 2006-2007, Ext JS, LLC.
16765  *
16766  * Originally Released Under LGPL - original licence link has changed is not relivant.
16767  *
16768  * Fork - LGPL
16769  * <script type="text/javascript">
16770  */
16771
16772 /**
16773  * @class Roo.data.ArrayReader
16774  * @extends Roo.data.DataReader
16775  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16776  * Each element of that Array represents a row of data fields. The
16777  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16778  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16779  * <p>
16780  * Example code:.
16781  * <pre><code>
16782 var RecordDef = Roo.data.Record.create([
16783     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16784     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16785 ]);
16786 var myReader = new Roo.data.ArrayReader({
16787     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16788 }, RecordDef);
16789 </code></pre>
16790  * <p>
16791  * This would consume an Array like this:
16792  * <pre><code>
16793 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16794   </code></pre>
16795  
16796  * @constructor
16797  * Create a new JsonReader
16798  * @param {Object} meta Metadata configuration options.
16799  * @param {Object|Array} recordType Either an Array of field definition objects
16800  * 
16801  * @cfg {Array} fields Array of field definition objects
16802  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16803  * as specified to {@link Roo.data.Record#create},
16804  * or an {@link Roo.data.Record} object
16805  *
16806  * 
16807  * created using {@link Roo.data.Record#create}.
16808  */
16809 Roo.data.ArrayReader = function(meta, recordType)
16810 {    
16811     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16812 };
16813
16814 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16815     
16816       /**
16817      * Create a data block containing Roo.data.Records from an XML document.
16818      * @param {Object} o An Array of row objects which represents the dataset.
16819      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16820      * a cache of Roo.data.Records.
16821      */
16822     readRecords : function(o)
16823     {
16824         var sid = this.meta ? this.meta.id : null;
16825         var recordType = this.recordType, fields = recordType.prototype.fields;
16826         var records = [];
16827         var root = o;
16828         for(var i = 0; i < root.length; i++){
16829             var n = root[i];
16830             var values = {};
16831             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16832             for(var j = 0, jlen = fields.length; j < jlen; j++){
16833                 var f = fields.items[j];
16834                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16835                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16836                 v = f.convert(v);
16837                 values[f.name] = v;
16838             }
16839             var record = new recordType(values, id);
16840             record.json = n;
16841             records[records.length] = record;
16842         }
16843         return {
16844             records : records,
16845             totalRecords : records.length
16846         };
16847     },
16848     // used when loading children.. @see loadDataFromChildren
16849     toLoadData: function(rec)
16850     {
16851         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16852         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16853         
16854     }
16855     
16856     
16857 });/*
16858  * - LGPL
16859  * * 
16860  */
16861
16862 /**
16863  * @class Roo.bootstrap.form.ComboBox
16864  * @extends Roo.bootstrap.form.TriggerField
16865  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16866  * @cfg {Boolean} append (true|false) default false
16867  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16868  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16869  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16870  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16871  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16872  * @cfg {Boolean} animate default true
16873  * @cfg {Boolean} emptyResultText only for touch device
16874  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16875  * @cfg {String} emptyTitle default ''
16876  * @cfg {Number} width fixed with? experimental
16877  * @constructor
16878  * Create a new ComboBox.
16879  * @param {Object} config Configuration options
16880  */
16881 Roo.bootstrap.form.ComboBox = function(config){
16882     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16883     this.addEvents({
16884         /**
16885          * @event expand
16886          * Fires when the dropdown list is expanded
16887         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888         */
16889         'expand' : true,
16890         /**
16891          * @event collapse
16892          * Fires when the dropdown list is collapsed
16893         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16894         */
16895         'collapse' : true,
16896         /**
16897          * @event beforeselect
16898          * Fires before a list item is selected. Return false to cancel the selection.
16899         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16900         * @param {Roo.data.Record} record The data record returned from the underlying store
16901         * @param {Number} index The index of the selected item in the dropdown list
16902         */
16903         'beforeselect' : true,
16904         /**
16905          * @event select
16906          * Fires when a list item is selected
16907         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16909         * @param {Number} index The index of the selected item in the dropdown list
16910         */
16911         'select' : true,
16912         /**
16913          * @event beforequery
16914          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16915          * The event object passed has these properties:
16916         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16917         * @param {String} query The query
16918         * @param {Boolean} forceAll true to force "all" query
16919         * @param {Boolean} cancel true to cancel the query
16920         * @param {Object} e The query event object
16921         */
16922         'beforequery': true,
16923          /**
16924          * @event add
16925          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16926         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927         */
16928         'add' : true,
16929         /**
16930          * @event edit
16931          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16932         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16934         */
16935         'edit' : true,
16936         /**
16937          * @event remove
16938          * Fires when the remove value from the combobox array
16939         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16940         */
16941         'remove' : true,
16942         /**
16943          * @event afterremove
16944          * Fires when the remove value from the combobox array
16945         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16946         */
16947         'afterremove' : true,
16948         /**
16949          * @event specialfilter
16950          * Fires when specialfilter
16951             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16952             */
16953         'specialfilter' : true,
16954         /**
16955          * @event tick
16956          * Fires when tick the element
16957             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16958             */
16959         'tick' : true,
16960         /**
16961          * @event touchviewdisplay
16962          * Fires when touch view require special display (default is using displayField)
16963             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16964             * @param {Object} cfg set html .
16965             */
16966         'touchviewdisplay' : true
16967         
16968     });
16969     
16970     this.item = [];
16971     this.tickItems = [];
16972     
16973     this.selectedIndex = -1;
16974     if(this.mode == 'local'){
16975         if(config.queryDelay === undefined){
16976             this.queryDelay = 10;
16977         }
16978         if(config.minChars === undefined){
16979             this.minChars = 0;
16980         }
16981     }
16982 };
16983
16984 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16985      
16986     /**
16987      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16988      * rendering into an Roo.Editor, defaults to false)
16989      */
16990     /**
16991      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16992      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16993      */
16994     /**
16995      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16996      */
16997     /**
16998      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16999      * the dropdown list (defaults to undefined, with no header element)
17000      */
17001
17002      /**
17003      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17004      */
17005      
17006      /**
17007      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17008      */
17009     listWidth: undefined,
17010     /**
17011      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17012      * mode = 'remote' or 'text' if mode = 'local')
17013      */
17014     displayField: undefined,
17015     
17016     /**
17017      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17018      * mode = 'remote' or 'value' if mode = 'local'). 
17019      * Note: use of a valueField requires the user make a selection
17020      * in order for a value to be mapped.
17021      */
17022     valueField: undefined,
17023     /**
17024      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17025      */
17026     modalTitle : '',
17027     
17028     /**
17029      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17030      * field's data value (defaults to the underlying DOM element's name)
17031      */
17032     hiddenName: undefined,
17033     /**
17034      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17035      */
17036     listClass: '',
17037     /**
17038      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17039      */
17040     selectedClass: 'active',
17041     
17042     /**
17043      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17044      */
17045     shadow:'sides',
17046     /**
17047      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17048      * anchor positions (defaults to 'tl-bl')
17049      */
17050     listAlign: 'tl-bl?',
17051     /**
17052      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17053      */
17054     maxHeight: 300,
17055     /**
17056      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17057      * query specified by the allQuery config option (defaults to 'query')
17058      */
17059     triggerAction: 'query',
17060     /**
17061      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17062      * (defaults to 4, does not apply if editable = false)
17063      */
17064     minChars : 4,
17065     /**
17066      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17067      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17068      */
17069     typeAhead: false,
17070     /**
17071      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17072      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17073      */
17074     queryDelay: 500,
17075     /**
17076      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17077      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17078      */
17079     pageSize: 0,
17080     /**
17081      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17082      * when editable = true (defaults to false)
17083      */
17084     selectOnFocus:false,
17085     /**
17086      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17087      */
17088     queryParam: 'query',
17089     /**
17090      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17091      * when mode = 'remote' (defaults to 'Loading...')
17092      */
17093     loadingText: 'Loading...',
17094     /**
17095      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17096      */
17097     resizable: false,
17098     /**
17099      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17100      */
17101     handleHeight : 8,
17102     /**
17103      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17104      * traditional select (defaults to true)
17105      */
17106     editable: true,
17107     /**
17108      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17109      */
17110     allQuery: '',
17111     /**
17112      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17113      */
17114     mode: 'remote',
17115     /**
17116      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17117      * listWidth has a higher value)
17118      */
17119     minListWidth : 70,
17120     /**
17121      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17122      * allow the user to set arbitrary text into the field (defaults to false)
17123      */
17124     forceSelection:false,
17125     /**
17126      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17127      * if typeAhead = true (defaults to 250)
17128      */
17129     typeAheadDelay : 250,
17130     /**
17131      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17132      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17133      */
17134     valueNotFoundText : undefined,
17135     /**
17136      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17137      */
17138     blockFocus : false,
17139     
17140     /**
17141      * @cfg {Boolean} disableClear Disable showing of clear button.
17142      */
17143     disableClear : false,
17144     /**
17145      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17146      */
17147     alwaysQuery : false,
17148     
17149     /**
17150      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17151      */
17152     multiple : false,
17153     
17154     /**
17155      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17156      */
17157     invalidClass : "has-warning",
17158     
17159     /**
17160      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17161      */
17162     validClass : "has-success",
17163     
17164     /**
17165      * @cfg {Boolean} specialFilter (true|false) special filter default false
17166      */
17167     specialFilter : false,
17168     
17169     /**
17170      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17171      */
17172     mobileTouchView : true,
17173     
17174     /**
17175      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17176      */
17177     useNativeIOS : false,
17178     
17179     /**
17180      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17181      */
17182     mobile_restrict_height : false,
17183     
17184     ios_options : false,
17185     
17186     //private
17187     addicon : false,
17188     editicon: false,
17189     
17190     page: 0,
17191     hasQuery: false,
17192     append: false,
17193     loadNext: false,
17194     autoFocus : true,
17195     tickable : false,
17196     btnPosition : 'right',
17197     triggerList : true,
17198     showToggleBtn : true,
17199     animate : true,
17200     emptyResultText: 'Empty',
17201     triggerText : 'Select',
17202     emptyTitle : '',
17203     width : false,
17204     
17205     // element that contains real text value.. (when hidden is used..)
17206     
17207     getAutoCreate : function()
17208     {   
17209         var cfg = false;
17210         //render
17211         /*
17212          * Render classic select for iso
17213          */
17214         
17215         if(Roo.isIOS && this.useNativeIOS){
17216             cfg = this.getAutoCreateNativeIOS();
17217             return cfg;
17218         }
17219         
17220         /*
17221          * Touch Devices
17222          */
17223         
17224         if(Roo.isTouch && this.mobileTouchView){
17225             cfg = this.getAutoCreateTouchView();
17226             return cfg;;
17227         }
17228         
17229         /*
17230          *  Normal ComboBox
17231          */
17232         if(!this.tickable){
17233             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17234             return cfg;
17235         }
17236         
17237         /*
17238          *  ComboBox with tickable selections
17239          */
17240              
17241         var align = this.labelAlign || this.parentLabelAlign();
17242         
17243         cfg = {
17244             cls : 'form-group roo-combobox-tickable' //input-group
17245         };
17246         
17247         var btn_text_select = '';
17248         var btn_text_done = '';
17249         var btn_text_cancel = '';
17250         
17251         if (this.btn_text_show) {
17252             btn_text_select = 'Select';
17253             btn_text_done = 'Done';
17254             btn_text_cancel = 'Cancel'; 
17255         }
17256         
17257         var buttons = {
17258             tag : 'div',
17259             cls : 'tickable-buttons',
17260             cn : [
17261                 {
17262                     tag : 'button',
17263                     type : 'button',
17264                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17265                     //html : this.triggerText
17266                     html: btn_text_select
17267                 },
17268                 {
17269                     tag : 'button',
17270                     type : 'button',
17271                     name : 'ok',
17272                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17273                     //html : 'Done'
17274                     html: btn_text_done
17275                 },
17276                 {
17277                     tag : 'button',
17278                     type : 'button',
17279                     name : 'cancel',
17280                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17281                     //html : 'Cancel'
17282                     html: btn_text_cancel
17283                 }
17284             ]
17285         };
17286         
17287         if(this.editable){
17288             buttons.cn.unshift({
17289                 tag: 'input',
17290                 cls: 'roo-select2-search-field-input'
17291             });
17292         }
17293         
17294         var _this = this;
17295         
17296         Roo.each(buttons.cn, function(c){
17297             if (_this.size) {
17298                 c.cls += ' btn-' + _this.size;
17299             }
17300
17301             if (_this.disabled) {
17302                 c.disabled = true;
17303             }
17304         });
17305         
17306         var box = {
17307             tag: 'div',
17308             style : 'display: contents',
17309             cn: [
17310                 {
17311                     tag: 'input',
17312                     type : 'hidden',
17313                     cls: 'form-hidden-field'
17314                 },
17315                 {
17316                     tag: 'ul',
17317                     cls: 'roo-select2-choices',
17318                     cn:[
17319                         {
17320                             tag: 'li',
17321                             cls: 'roo-select2-search-field',
17322                             cn: [
17323                                 buttons
17324                             ]
17325                         }
17326                     ]
17327                 }
17328             ]
17329         };
17330         
17331         var combobox = {
17332             cls: 'roo-select2-container input-group roo-select2-container-multi',
17333             cn: [
17334                 
17335                 box
17336 //                {
17337 //                    tag: 'ul',
17338 //                    cls: 'typeahead typeahead-long dropdown-menu',
17339 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17340 //                }
17341             ]
17342         };
17343         
17344         if(this.hasFeedback && !this.allowBlank){
17345             
17346             var feedback = {
17347                 tag: 'span',
17348                 cls: 'glyphicon form-control-feedback'
17349             };
17350
17351             combobox.cn.push(feedback);
17352         }
17353         
17354         
17355         
17356         var indicator = {
17357             tag : 'i',
17358             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17359             tooltip : 'This field is required'
17360         };
17361         if (Roo.bootstrap.version == 4) {
17362             indicator = {
17363                 tag : 'i',
17364                 style : 'display:none'
17365             };
17366         }
17367         if (align ==='left' && this.fieldLabel.length) {
17368             
17369             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17370             
17371             cfg.cn = [
17372                 indicator,
17373                 {
17374                     tag: 'label',
17375                     'for' :  id,
17376                     cls : 'control-label col-form-label',
17377                     html : this.fieldLabel
17378
17379                 },
17380                 {
17381                     cls : "", 
17382                     cn: [
17383                         combobox
17384                     ]
17385                 }
17386
17387             ];
17388             
17389             var labelCfg = cfg.cn[1];
17390             var contentCfg = cfg.cn[2];
17391             
17392
17393             if(this.indicatorpos == 'right'){
17394                 
17395                 cfg.cn = [
17396                     {
17397                         tag: 'label',
17398                         'for' :  id,
17399                         cls : 'control-label col-form-label',
17400                         cn : [
17401                             {
17402                                 tag : 'span',
17403                                 html : this.fieldLabel
17404                             },
17405                             indicator
17406                         ]
17407                     },
17408                     {
17409                         cls : "",
17410                         cn: [
17411                             combobox
17412                         ]
17413                     }
17414
17415                 ];
17416                 
17417                 
17418                 
17419                 labelCfg = cfg.cn[0];
17420                 contentCfg = cfg.cn[1];
17421             
17422             }
17423             
17424             if(this.labelWidth > 12){
17425                 labelCfg.style = "width: " + this.labelWidth + 'px';
17426             }
17427             if(this.width * 1 > 0){
17428                 contentCfg.style = "width: " + this.width + 'px';
17429             }
17430             if(this.labelWidth < 13 && this.labelmd == 0){
17431                 this.labelmd = this.labelWidth;
17432             }
17433             
17434             if(this.labellg > 0){
17435                 labelCfg.cls += ' col-lg-' + this.labellg;
17436                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17437             }
17438             
17439             if(this.labelmd > 0){
17440                 labelCfg.cls += ' col-md-' + this.labelmd;
17441                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17442             }
17443             
17444             if(this.labelsm > 0){
17445                 labelCfg.cls += ' col-sm-' + this.labelsm;
17446                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17447             }
17448             
17449             if(this.labelxs > 0){
17450                 labelCfg.cls += ' col-xs-' + this.labelxs;
17451                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17452             }
17453                 
17454                 
17455         } else if ( this.fieldLabel.length) {
17456 //                Roo.log(" label");
17457                  cfg.cn = [
17458                    indicator,
17459                     {
17460                         tag: 'label',
17461                         //cls : 'input-group-addon',
17462                         html : this.fieldLabel
17463                     },
17464                     combobox
17465                 ];
17466                 
17467                 if(this.indicatorpos == 'right'){
17468                     cfg.cn = [
17469                         {
17470                             tag: 'label',
17471                             //cls : 'input-group-addon',
17472                             html : this.fieldLabel
17473                         },
17474                         indicator,
17475                         combobox
17476                     ];
17477                     
17478                 }
17479
17480         } else {
17481             
17482 //                Roo.log(" no label && no align");
17483                 cfg = combobox
17484                      
17485                 
17486         }
17487          
17488         var settings=this;
17489         ['xs','sm','md','lg'].map(function(size){
17490             if (settings[size]) {
17491                 cfg.cls += ' col-' + size + '-' + settings[size];
17492             }
17493         });
17494         
17495         return cfg;
17496         
17497     },
17498     
17499     _initEventsCalled : false,
17500     
17501     // private
17502     initEvents: function()
17503     {   
17504         if (this._initEventsCalled) { // as we call render... prevent looping...
17505             return;
17506         }
17507         this._initEventsCalled = true;
17508         
17509         if (!this.store) {
17510             throw "can not find store for combo";
17511         }
17512         
17513         this.indicator = this.indicatorEl();
17514         
17515         this.store = Roo.factory(this.store, Roo.data);
17516         this.store.parent = this;
17517         
17518         // if we are building from html. then this element is so complex, that we can not really
17519         // use the rendered HTML.
17520         // so we have to trash and replace the previous code.
17521         if (Roo.XComponent.build_from_html) {
17522             // remove this element....
17523             var e = this.el.dom, k=0;
17524             while (e ) { e = e.previousSibling;  ++k;}
17525
17526             this.el.remove();
17527             
17528             this.el=false;
17529             this.rendered = false;
17530             
17531             this.render(this.parent().getChildContainer(true), k);
17532         }
17533         
17534         if(Roo.isIOS && this.useNativeIOS){
17535             this.initIOSView();
17536             return;
17537         }
17538         
17539         /*
17540          * Touch Devices
17541          */
17542         
17543         if(Roo.isTouch && this.mobileTouchView){
17544             this.initTouchView();
17545             return;
17546         }
17547         
17548         if(this.tickable){
17549             this.initTickableEvents();
17550             return;
17551         }
17552         
17553         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17554         
17555         if(this.hiddenName){
17556             
17557             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17558             
17559             this.hiddenField.dom.value =
17560                 this.hiddenValue !== undefined ? this.hiddenValue :
17561                 this.value !== undefined ? this.value : '';
17562
17563             // prevent input submission
17564             this.el.dom.removeAttribute('name');
17565             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17566              
17567              
17568         }
17569         //if(Roo.isGecko){
17570         //    this.el.dom.setAttribute('autocomplete', 'off');
17571         //}
17572         
17573         var cls = 'x-combo-list';
17574         
17575         //this.list = new Roo.Layer({
17576         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17577         //});
17578         
17579         var _this = this;
17580         
17581         (function(){
17582             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17583             _this.list.setWidth(lw);
17584         }).defer(100);
17585         
17586         this.list.on('mouseover', this.onViewOver, this);
17587         this.list.on('mousemove', this.onViewMove, this);
17588         this.list.on('scroll', this.onViewScroll, this);
17589         
17590         /*
17591         this.list.swallowEvent('mousewheel');
17592         this.assetHeight = 0;
17593
17594         if(this.title){
17595             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17596             this.assetHeight += this.header.getHeight();
17597         }
17598
17599         this.innerList = this.list.createChild({cls:cls+'-inner'});
17600         this.innerList.on('mouseover', this.onViewOver, this);
17601         this.innerList.on('mousemove', this.onViewMove, this);
17602         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17603         
17604         if(this.allowBlank && !this.pageSize && !this.disableClear){
17605             this.footer = this.list.createChild({cls:cls+'-ft'});
17606             this.pageTb = new Roo.Toolbar(this.footer);
17607            
17608         }
17609         if(this.pageSize){
17610             this.footer = this.list.createChild({cls:cls+'-ft'});
17611             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17612                     {pageSize: this.pageSize});
17613             
17614         }
17615         
17616         if (this.pageTb && this.allowBlank && !this.disableClear) {
17617             var _this = this;
17618             this.pageTb.add(new Roo.Toolbar.Fill(), {
17619                 cls: 'x-btn-icon x-btn-clear',
17620                 text: '&#160;',
17621                 handler: function()
17622                 {
17623                     _this.collapse();
17624                     _this.clearValue();
17625                     _this.onSelect(false, -1);
17626                 }
17627             });
17628         }
17629         if (this.footer) {
17630             this.assetHeight += this.footer.getHeight();
17631         }
17632         */
17633             
17634         if(!this.tpl){
17635             this.tpl = Roo.bootstrap.version == 4 ?
17636                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17637                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17638         }
17639
17640         this.view = new Roo.View(this.list, this.tpl, {
17641             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17642         });
17643         //this.view.wrapEl.setDisplayed(false);
17644         this.view.on('click', this.onViewClick, this);
17645         
17646         
17647         this.store.on('beforeload', this.onBeforeLoad, this);
17648         this.store.on('load', this.onLoad, this);
17649         this.store.on('loadexception', this.onLoadException, this);
17650         /*
17651         if(this.resizable){
17652             this.resizer = new Roo.Resizable(this.list,  {
17653                pinned:true, handles:'se'
17654             });
17655             this.resizer.on('resize', function(r, w, h){
17656                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17657                 this.listWidth = w;
17658                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17659                 this.restrictHeight();
17660             }, this);
17661             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17662         }
17663         */
17664         if(!this.editable){
17665             this.editable = true;
17666             this.setEditable(false);
17667         }
17668         
17669         /*
17670         
17671         if (typeof(this.events.add.listeners) != 'undefined') {
17672             
17673             this.addicon = this.wrap.createChild(
17674                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17675        
17676             this.addicon.on('click', function(e) {
17677                 this.fireEvent('add', this);
17678             }, this);
17679         }
17680         if (typeof(this.events.edit.listeners) != 'undefined') {
17681             
17682             this.editicon = this.wrap.createChild(
17683                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17684             if (this.addicon) {
17685                 this.editicon.setStyle('margin-left', '40px');
17686             }
17687             this.editicon.on('click', function(e) {
17688                 
17689                 // we fire even  if inothing is selected..
17690                 this.fireEvent('edit', this, this.lastData );
17691                 
17692             }, this);
17693         }
17694         */
17695         
17696         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17697             "up" : function(e){
17698                 this.inKeyMode = true;
17699                 this.selectPrev();
17700             },
17701
17702             "down" : function(e){
17703                 if(!this.isExpanded()){
17704                     this.onTriggerClick();
17705                 }else{
17706                     this.inKeyMode = true;
17707                     this.selectNext();
17708                 }
17709             },
17710
17711             "enter" : function(e){
17712 //                this.onViewClick();
17713                 //return true;
17714                 this.collapse();
17715                 
17716                 if(this.fireEvent("specialkey", this, e)){
17717                     this.onViewClick(false);
17718                 }
17719                 
17720                 return true;
17721             },
17722
17723             "esc" : function(e){
17724                 this.collapse();
17725             },
17726
17727             "tab" : function(e){
17728                 this.collapse();
17729                 
17730                 if(this.fireEvent("specialkey", this, e)){
17731                     this.onViewClick(false);
17732                 }
17733                 
17734                 return true;
17735             },
17736
17737             scope : this,
17738
17739             doRelay : function(foo, bar, hname){
17740                 if(hname == 'down' || this.scope.isExpanded()){
17741                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17742                 }
17743                 return true;
17744             },
17745
17746             forceKeyDown: true
17747         });
17748         
17749         
17750         this.queryDelay = Math.max(this.queryDelay || 10,
17751                 this.mode == 'local' ? 10 : 250);
17752         
17753         
17754         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17755         
17756         if(this.typeAhead){
17757             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17758         }
17759         if(this.editable !== false){
17760             this.inputEl().on("keyup", this.onKeyUp, this);
17761         }
17762         if(this.forceSelection){
17763             this.inputEl().on('blur', this.doForce, this);
17764         }
17765         
17766         if(this.multiple){
17767             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17768             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17769         }
17770     },
17771     
17772     initTickableEvents: function()
17773     {   
17774         this.createList();
17775         
17776         if(this.hiddenName){
17777             
17778             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17779             
17780             this.hiddenField.dom.value =
17781                 this.hiddenValue !== undefined ? this.hiddenValue :
17782                 this.value !== undefined ? this.value : '';
17783
17784             // prevent input submission
17785             this.el.dom.removeAttribute('name');
17786             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17787              
17788              
17789         }
17790         
17791 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17792         
17793         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17794         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17795         if(this.triggerList){
17796             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17797         }
17798          
17799         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17800         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17801         
17802         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17803         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17804         
17805         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17806         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17807         
17808         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17809         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17810         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17811         
17812         this.okBtn.hide();
17813         this.cancelBtn.hide();
17814         
17815         var _this = this;
17816         
17817         (function(){
17818             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17819             _this.list.setWidth(lw);
17820         }).defer(100);
17821         
17822         this.list.on('mouseover', this.onViewOver, this);
17823         this.list.on('mousemove', this.onViewMove, this);
17824         
17825         this.list.on('scroll', this.onViewScroll, this);
17826         
17827         if(!this.tpl){
17828             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17829                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17830         }
17831
17832         this.view = new Roo.View(this.list, this.tpl, {
17833             singleSelect:true,
17834             tickable:true,
17835             parent:this,
17836             store: this.store,
17837             selectedClass: this.selectedClass
17838         });
17839         
17840         //this.view.wrapEl.setDisplayed(false);
17841         this.view.on('click', this.onViewClick, this);
17842         
17843         
17844         
17845         this.store.on('beforeload', this.onBeforeLoad, this);
17846         this.store.on('load', this.onLoad, this);
17847         this.store.on('loadexception', this.onLoadException, this);
17848         
17849         if(this.editable){
17850             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17851                 "up" : function(e){
17852                     this.inKeyMode = true;
17853                     this.selectPrev();
17854                 },
17855
17856                 "down" : function(e){
17857                     this.inKeyMode = true;
17858                     this.selectNext();
17859                 },
17860
17861                 "enter" : function(e){
17862                     if(this.fireEvent("specialkey", this, e)){
17863                         this.onViewClick(false);
17864                     }
17865                     
17866                     return true;
17867                 },
17868
17869                 "esc" : function(e){
17870                     this.onTickableFooterButtonClick(e, false, false);
17871                 },
17872
17873                 "tab" : function(e){
17874                     this.fireEvent("specialkey", this, e);
17875                     
17876                     this.onTickableFooterButtonClick(e, false, false);
17877                     
17878                     return true;
17879                 },
17880
17881                 scope : this,
17882
17883                 doRelay : function(e, fn, key){
17884                     if(this.scope.isExpanded()){
17885                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17886                     }
17887                     return true;
17888                 },
17889
17890                 forceKeyDown: true
17891             });
17892         }
17893         
17894         this.queryDelay = Math.max(this.queryDelay || 10,
17895                 this.mode == 'local' ? 10 : 250);
17896         
17897         
17898         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17899         
17900         if(this.typeAhead){
17901             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17902         }
17903         
17904         if(this.editable !== false){
17905             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17906         }
17907         
17908         this.indicator = this.indicatorEl();
17909         
17910         if(this.indicator){
17911             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17912             this.indicator.hide();
17913         }
17914         
17915     },
17916
17917     onDestroy : function(){
17918         if(this.view){
17919             this.view.setStore(null);
17920             this.view.el.removeAllListeners();
17921             this.view.el.remove();
17922             this.view.purgeListeners();
17923         }
17924         if(this.list){
17925             this.list.dom.innerHTML  = '';
17926         }
17927         
17928         if(this.store){
17929             this.store.un('beforeload', this.onBeforeLoad, this);
17930             this.store.un('load', this.onLoad, this);
17931             this.store.un('loadexception', this.onLoadException, this);
17932         }
17933         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17934     },
17935
17936     // private
17937     fireKey : function(e){
17938         if(e.isNavKeyPress() && !this.list.isVisible()){
17939             this.fireEvent("specialkey", this, e);
17940         }
17941     },
17942
17943     // private
17944     onResize: function(w, h)
17945     {
17946         
17947         
17948 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17949 //        
17950 //        if(typeof w != 'number'){
17951 //            // we do not handle it!?!?
17952 //            return;
17953 //        }
17954 //        var tw = this.trigger.getWidth();
17955 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17956 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17957 //        var x = w - tw;
17958 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17959 //            
17960 //        //this.trigger.setStyle('left', x+'px');
17961 //        
17962 //        if(this.list && this.listWidth === undefined){
17963 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17964 //            this.list.setWidth(lw);
17965 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17966 //        }
17967         
17968     
17969         
17970     },
17971
17972     /**
17973      * Allow or prevent the user from directly editing the field text.  If false is passed,
17974      * the user will only be able to select from the items defined in the dropdown list.  This method
17975      * is the runtime equivalent of setting the 'editable' config option at config time.
17976      * @param {Boolean} value True to allow the user to directly edit the field text
17977      */
17978     setEditable : function(value){
17979         if(value == this.editable){
17980             return;
17981         }
17982         this.editable = value;
17983         if(!value){
17984             this.inputEl().dom.setAttribute('readOnly', true);
17985             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17986             this.inputEl().addClass('x-combo-noedit');
17987         }else{
17988             this.inputEl().dom.removeAttribute('readOnly');
17989             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17990             this.inputEl().removeClass('x-combo-noedit');
17991         }
17992     },
17993
17994     // private
17995     
17996     onBeforeLoad : function(combo,opts){
17997         if(!this.hasFocus){
17998             return;
17999         }
18000          if (!opts.add) {
18001             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18002          }
18003         this.restrictHeight();
18004         this.selectedIndex = -1;
18005     },
18006
18007     // private
18008     onLoad : function(){
18009         
18010         this.hasQuery = false;
18011         
18012         if(!this.hasFocus){
18013             return;
18014         }
18015         
18016         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18017             this.loading.hide();
18018         }
18019         
18020         if(this.store.getCount() > 0){
18021             
18022             this.expand();
18023             this.restrictHeight();
18024             if(this.lastQuery == this.allQuery){
18025                 if(this.editable && !this.tickable){
18026                     this.inputEl().dom.select();
18027                 }
18028                 
18029                 if(
18030                     !this.selectByValue(this.value, true) &&
18031                     this.autoFocus && 
18032                     (
18033                         !this.store.lastOptions ||
18034                         typeof(this.store.lastOptions.add) == 'undefined' || 
18035                         this.store.lastOptions.add != true
18036                     )
18037                 ){
18038                     this.select(0, true);
18039                 }
18040             }else{
18041                 if(this.autoFocus){
18042                     this.selectNext();
18043                 }
18044                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18045                     this.taTask.delay(this.typeAheadDelay);
18046                 }
18047             }
18048         }else{
18049             this.onEmptyResults();
18050         }
18051         
18052         //this.el.focus();
18053     },
18054     // private
18055     onLoadException : function()
18056     {
18057         this.hasQuery = false;
18058         
18059         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18060             this.loading.hide();
18061         }
18062         
18063         if(this.tickable && this.editable){
18064             return;
18065         }
18066         
18067         this.collapse();
18068         // only causes errors at present
18069         //Roo.log(this.store.reader.jsonData);
18070         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18071             // fixme
18072             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18073         //}
18074         
18075         
18076     },
18077     // private
18078     onTypeAhead : function(){
18079         if(this.store.getCount() > 0){
18080             var r = this.store.getAt(0);
18081             var newValue = r.data[this.displayField];
18082             var len = newValue.length;
18083             var selStart = this.getRawValue().length;
18084             
18085             if(selStart != len){
18086                 this.setRawValue(newValue);
18087                 this.selectText(selStart, newValue.length);
18088             }
18089         }
18090     },
18091
18092     // private
18093     onSelect : function(record, index){
18094         
18095         if(this.fireEvent('beforeselect', this, record, index) !== false){
18096         
18097             this.setFromData(index > -1 ? record.data : false);
18098             
18099             this.collapse();
18100             this.fireEvent('select', this, record, index);
18101         }
18102     },
18103
18104     /**
18105      * Returns the currently selected field value or empty string if no value is set.
18106      * @return {String} value The selected value
18107      */
18108     getValue : function()
18109     {
18110         if(Roo.isIOS && this.useNativeIOS){
18111             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18112         }
18113         
18114         if(this.multiple){
18115             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18116         }
18117         
18118         if(this.valueField){
18119             return typeof this.value != 'undefined' ? this.value : '';
18120         }else{
18121             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18122         }
18123     },
18124     
18125     getRawValue : function()
18126     {
18127         if(Roo.isIOS && this.useNativeIOS){
18128             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18129         }
18130         
18131         var v = this.inputEl().getValue();
18132         
18133         return v;
18134     },
18135
18136     /**
18137      * Clears any text/value currently set in the field
18138      */
18139     clearValue : function(){
18140         
18141         if(this.hiddenField){
18142             this.hiddenField.dom.value = '';
18143         }
18144         this.value = '';
18145         this.setRawValue('');
18146         this.lastSelectionText = '';
18147         this.lastData = false;
18148         
18149         var close = this.closeTriggerEl();
18150         
18151         if(close){
18152             close.hide();
18153         }
18154         
18155         this.validate();
18156         
18157     },
18158
18159     /**
18160      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18161      * will be displayed in the field.  If the value does not match the data value of an existing item,
18162      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18163      * Otherwise the field will be blank (although the value will still be set).
18164      * @param {String} value The value to match
18165      */
18166     setValue : function(v)
18167     {
18168         if(Roo.isIOS && this.useNativeIOS){
18169             this.setIOSValue(v);
18170             return;
18171         }
18172         
18173         if(this.multiple){
18174             this.syncValue();
18175             return;
18176         }
18177         
18178         var text = v;
18179         if(this.valueField){
18180             var r = this.findRecord(this.valueField, v);
18181             if(r){
18182                 text = r.data[this.displayField];
18183             }else if(this.valueNotFoundText !== undefined){
18184                 text = this.valueNotFoundText;
18185             }
18186         }
18187         this.lastSelectionText = text;
18188         if(this.hiddenField){
18189             this.hiddenField.dom.value = v;
18190         }
18191         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18192         this.value = v;
18193         
18194         var close = this.closeTriggerEl();
18195         
18196         if(close){
18197             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18198         }
18199         
18200         this.validate();
18201     },
18202     /**
18203      * @property {Object} the last set data for the element
18204      */
18205     
18206     lastData : false,
18207     /**
18208      * Sets the value of the field based on a object which is related to the record format for the store.
18209      * @param {Object} value the value to set as. or false on reset?
18210      */
18211     setFromData : function(o){
18212         
18213         if(this.multiple){
18214             this.addItem(o);
18215             return;
18216         }
18217             
18218         var dv = ''; // display value
18219         var vv = ''; // value value..
18220         this.lastData = o;
18221         if (this.displayField) {
18222             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18223         } else {
18224             // this is an error condition!!!
18225             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18226         }
18227         
18228         if(this.valueField){
18229             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18230         }
18231         
18232         var close = this.closeTriggerEl();
18233         
18234         if(close){
18235             if(dv.length || vv * 1 > 0){
18236                 close.show() ;
18237                 this.blockFocus=true;
18238             } else {
18239                 close.hide();
18240             }             
18241         }
18242         
18243         if(this.hiddenField){
18244             this.hiddenField.dom.value = vv;
18245             
18246             this.lastSelectionText = dv;
18247             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18248             this.value = vv;
18249             return;
18250         }
18251         // no hidden field.. - we store the value in 'value', but still display
18252         // display field!!!!
18253         this.lastSelectionText = dv;
18254         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18255         this.value = vv;
18256         
18257         
18258         
18259     },
18260     // private
18261     reset : function(){
18262         // overridden so that last data is reset..
18263         
18264         if(this.multiple){
18265             this.clearItem();
18266             return;
18267         }
18268         
18269         this.setValue(this.originalValue);
18270         //this.clearInvalid();
18271         this.lastData = false;
18272         if (this.view) {
18273             this.view.clearSelections();
18274         }
18275         
18276         this.validate();
18277     },
18278     // private
18279     findRecord : function(prop, value){
18280         var record;
18281         if(this.store.getCount() > 0){
18282             this.store.each(function(r){
18283                 if(r.data[prop] == value){
18284                     record = r;
18285                     return false;
18286                 }
18287                 return true;
18288             });
18289         }
18290         return record;
18291     },
18292     
18293     getName: function()
18294     {
18295         // returns hidden if it's set..
18296         if (!this.rendered) {return ''};
18297         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18298         
18299     },
18300     // private
18301     onViewMove : function(e, t){
18302         this.inKeyMode = false;
18303     },
18304
18305     // private
18306     onViewOver : function(e, t){
18307         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18308             return;
18309         }
18310         var item = this.view.findItemFromChild(t);
18311         
18312         if(item){
18313             var index = this.view.indexOf(item);
18314             this.select(index, false);
18315         }
18316     },
18317
18318     // private
18319     onViewClick : function(view, doFocus, el, e)
18320     {
18321         var index = this.view.getSelectedIndexes()[0];
18322         
18323         var r = this.store.getAt(index);
18324         
18325         if(this.tickable){
18326             
18327             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18328                 return;
18329             }
18330             
18331             var rm = false;
18332             var _this = this;
18333             
18334             Roo.each(this.tickItems, function(v,k){
18335                 
18336                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18337                     Roo.log(v);
18338                     _this.tickItems.splice(k, 1);
18339                     
18340                     if(typeof(e) == 'undefined' && view == false){
18341                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18342                     }
18343                     
18344                     rm = true;
18345                     return;
18346                 }
18347             });
18348             
18349             if(rm){
18350                 return;
18351             }
18352             
18353             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18354                 this.tickItems.push(r.data);
18355             }
18356             
18357             if(typeof(e) == 'undefined' && view == false){
18358                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18359             }
18360                     
18361             return;
18362         }
18363         
18364         if(r){
18365             this.onSelect(r, index);
18366         }
18367         if(doFocus !== false && !this.blockFocus){
18368             this.inputEl().focus();
18369         }
18370     },
18371
18372     // private
18373     restrictHeight : function(){
18374         //this.innerList.dom.style.height = '';
18375         //var inner = this.innerList.dom;
18376         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18377         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18378         //this.list.beginUpdate();
18379         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18380         this.list.alignTo(this.inputEl(), this.listAlign);
18381         this.list.alignTo(this.inputEl(), this.listAlign);
18382         //this.list.endUpdate();
18383     },
18384
18385     // private
18386     onEmptyResults : function(){
18387         
18388         if(this.tickable && this.editable){
18389             this.hasFocus = false;
18390             this.restrictHeight();
18391             return;
18392         }
18393         
18394         this.collapse();
18395     },
18396
18397     /**
18398      * Returns true if the dropdown list is expanded, else false.
18399      */
18400     isExpanded : function(){
18401         return this.list.isVisible();
18402     },
18403
18404     /**
18405      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18406      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18407      * @param {String} value The data value of the item to select
18408      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18409      * selected item if it is not currently in view (defaults to true)
18410      * @return {Boolean} True if the value matched an item in the list, else false
18411      */
18412     selectByValue : function(v, scrollIntoView){
18413         if(v !== undefined && v !== null){
18414             var r = this.findRecord(this.valueField || this.displayField, v);
18415             if(r){
18416                 this.select(this.store.indexOf(r), scrollIntoView);
18417                 return true;
18418             }
18419         }
18420         return false;
18421     },
18422
18423     /**
18424      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18425      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18426      * @param {Number} index The zero-based index of the list item to select
18427      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18428      * selected item if it is not currently in view (defaults to true)
18429      */
18430     select : function(index, scrollIntoView){
18431         this.selectedIndex = index;
18432         this.view.select(index);
18433         if(scrollIntoView !== false){
18434             var el = this.view.getNode(index);
18435             /*
18436              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18437              */
18438             if(el){
18439                 this.list.scrollChildIntoView(el, false);
18440             }
18441         }
18442     },
18443
18444     // private
18445     selectNext : function(){
18446         var ct = this.store.getCount();
18447         if(ct > 0){
18448             if(this.selectedIndex == -1){
18449                 this.select(0);
18450             }else if(this.selectedIndex < ct-1){
18451                 this.select(this.selectedIndex+1);
18452             }
18453         }
18454     },
18455
18456     // private
18457     selectPrev : function(){
18458         var ct = this.store.getCount();
18459         if(ct > 0){
18460             if(this.selectedIndex == -1){
18461                 this.select(0);
18462             }else if(this.selectedIndex != 0){
18463                 this.select(this.selectedIndex-1);
18464             }
18465         }
18466     },
18467
18468     // private
18469     onKeyUp : function(e){
18470         if(this.editable !== false && !e.isSpecialKey()){
18471             this.lastKey = e.getKey();
18472             this.dqTask.delay(this.queryDelay);
18473         }
18474     },
18475
18476     // private
18477     validateBlur : function(){
18478         return !this.list || !this.list.isVisible();   
18479     },
18480
18481     // private
18482     initQuery : function(){
18483         
18484         var v = this.getRawValue();
18485         
18486         if(this.tickable && this.editable){
18487             v = this.tickableInputEl().getValue();
18488         }
18489         
18490         this.doQuery(v);
18491     },
18492
18493     // private
18494     doForce : function(){
18495         if(this.inputEl().dom.value.length > 0){
18496             this.inputEl().dom.value =
18497                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18498              
18499         }
18500     },
18501
18502     /**
18503      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18504      * query allowing the query action to be canceled if needed.
18505      * @param {String} query The SQL query to execute
18506      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18507      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18508      * saved in the current store (defaults to false)
18509      */
18510     doQuery : function(q, forceAll){
18511         
18512         if(q === undefined || q === null){
18513             q = '';
18514         }
18515         var qe = {
18516             query: q,
18517             forceAll: forceAll,
18518             combo: this,
18519             cancel:false
18520         };
18521         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18522             return false;
18523         }
18524         q = qe.query;
18525         
18526         forceAll = qe.forceAll;
18527         if(forceAll === true || (q.length >= this.minChars)){
18528             
18529             this.hasQuery = true;
18530             
18531             if(this.lastQuery != q || this.alwaysQuery){
18532                 this.lastQuery = q;
18533                 if(this.mode == 'local'){
18534                     this.selectedIndex = -1;
18535                     if(forceAll){
18536                         this.store.clearFilter();
18537                     }else{
18538                         
18539                         if(this.specialFilter){
18540                             this.fireEvent('specialfilter', this);
18541                             this.onLoad();
18542                             return;
18543                         }
18544                         
18545                         this.store.filter(this.displayField, q);
18546                     }
18547                     
18548                     this.store.fireEvent("datachanged", this.store);
18549                     
18550                     this.onLoad();
18551                     
18552                     
18553                 }else{
18554                     
18555                     this.store.baseParams[this.queryParam] = q;
18556                     
18557                     var options = {params : this.getParams(q)};
18558                     
18559                     if(this.loadNext){
18560                         options.add = true;
18561                         options.params.start = this.page * this.pageSize;
18562                     }
18563                     
18564                     this.store.load(options);
18565                     
18566                     /*
18567                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18568                      *  we should expand the list on onLoad
18569                      *  so command out it
18570                      */
18571 //                    this.expand();
18572                 }
18573             }else{
18574                 this.selectedIndex = -1;
18575                 this.onLoad();   
18576             }
18577         }
18578         
18579         this.loadNext = false;
18580     },
18581     
18582     // private
18583     getParams : function(q){
18584         var p = {};
18585         //p[this.queryParam] = q;
18586         
18587         if(this.pageSize){
18588             p.start = 0;
18589             p.limit = this.pageSize;
18590         }
18591         return p;
18592     },
18593
18594     /**
18595      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18596      */
18597     collapse : function(){
18598         if(!this.isExpanded()){
18599             return;
18600         }
18601         
18602         this.list.hide();
18603         
18604         this.hasFocus = false;
18605         
18606         if(this.tickable){
18607             this.okBtn.hide();
18608             this.cancelBtn.hide();
18609             this.trigger.show();
18610             
18611             if(this.editable){
18612                 this.tickableInputEl().dom.value = '';
18613                 this.tickableInputEl().blur();
18614             }
18615             
18616         }
18617         
18618         Roo.get(document).un('mousedown', this.collapseIf, this);
18619         Roo.get(document).un('mousewheel', this.collapseIf, this);
18620         if (!this.editable) {
18621             Roo.get(document).un('keydown', this.listKeyPress, this);
18622         }
18623         this.fireEvent('collapse', this);
18624         
18625         this.validate();
18626     },
18627
18628     // private
18629     collapseIf : function(e){
18630         var in_combo  = e.within(this.el);
18631         var in_list =  e.within(this.list);
18632         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18633         
18634         if (in_combo || in_list || is_list) {
18635             //e.stopPropagation();
18636             return;
18637         }
18638         
18639         if(this.tickable){
18640             this.onTickableFooterButtonClick(e, false, false);
18641         }
18642
18643         this.collapse();
18644         
18645     },
18646
18647     /**
18648      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18649      */
18650     expand : function(){
18651        
18652         if(this.isExpanded() || !this.hasFocus){
18653             return;
18654         }
18655         
18656         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18657         this.list.setWidth(lw);
18658         
18659         Roo.log('expand');
18660         
18661         this.list.show();
18662         
18663         this.restrictHeight();
18664         
18665         if(this.tickable){
18666             
18667             this.tickItems = Roo.apply([], this.item);
18668             
18669             this.okBtn.show();
18670             this.cancelBtn.show();
18671             this.trigger.hide();
18672             
18673             if(this.editable){
18674                 this.tickableInputEl().focus();
18675             }
18676             
18677         }
18678         
18679         Roo.get(document).on('mousedown', this.collapseIf, this);
18680         Roo.get(document).on('mousewheel', this.collapseIf, this);
18681         if (!this.editable) {
18682             Roo.get(document).on('keydown', this.listKeyPress, this);
18683         }
18684         
18685         this.fireEvent('expand', this);
18686     },
18687
18688     // private
18689     // Implements the default empty TriggerField.onTriggerClick function
18690     onTriggerClick : function(e)
18691     {
18692         Roo.log('trigger click');
18693         
18694         if(this.disabled || !this.triggerList){
18695             return;
18696         }
18697         
18698         this.page = 0;
18699         this.loadNext = false;
18700         
18701         if(this.isExpanded()){
18702             this.collapse();
18703             if (!this.blockFocus) {
18704                 this.inputEl().focus();
18705             }
18706             
18707         }else {
18708             this.hasFocus = true;
18709             if(this.triggerAction == 'all') {
18710                 this.doQuery(this.allQuery, true);
18711             } else {
18712                 this.doQuery(this.getRawValue());
18713             }
18714             if (!this.blockFocus) {
18715                 this.inputEl().focus();
18716             }
18717         }
18718     },
18719     
18720     onTickableTriggerClick : function(e)
18721     {
18722         if(this.disabled){
18723             return;
18724         }
18725         
18726         this.page = 0;
18727         this.loadNext = false;
18728         this.hasFocus = true;
18729         
18730         if(this.triggerAction == 'all') {
18731             this.doQuery(this.allQuery, true);
18732         } else {
18733             this.doQuery(this.getRawValue());
18734         }
18735     },
18736     
18737     onSearchFieldClick : function(e)
18738     {
18739         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18740             this.onTickableFooterButtonClick(e, false, false);
18741             return;
18742         }
18743         
18744         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18745             return;
18746         }
18747         
18748         this.page = 0;
18749         this.loadNext = false;
18750         this.hasFocus = true;
18751         
18752         if(this.triggerAction == 'all') {
18753             this.doQuery(this.allQuery, true);
18754         } else {
18755             this.doQuery(this.getRawValue());
18756         }
18757     },
18758     
18759     listKeyPress : function(e)
18760     {
18761         //Roo.log('listkeypress');
18762         // scroll to first matching element based on key pres..
18763         if (e.isSpecialKey()) {
18764             return false;
18765         }
18766         var k = String.fromCharCode(e.getKey()).toUpperCase();
18767         //Roo.log(k);
18768         var match  = false;
18769         var csel = this.view.getSelectedNodes();
18770         var cselitem = false;
18771         if (csel.length) {
18772             var ix = this.view.indexOf(csel[0]);
18773             cselitem  = this.store.getAt(ix);
18774             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18775                 cselitem = false;
18776             }
18777             
18778         }
18779         
18780         this.store.each(function(v) { 
18781             if (cselitem) {
18782                 // start at existing selection.
18783                 if (cselitem.id == v.id) {
18784                     cselitem = false;
18785                 }
18786                 return true;
18787             }
18788                 
18789             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18790                 match = this.store.indexOf(v);
18791                 return false;
18792             }
18793             return true;
18794         }, this);
18795         
18796         if (match === false) {
18797             return true; // no more action?
18798         }
18799         // scroll to?
18800         this.view.select(match);
18801         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18802         sn.scrollIntoView(sn.dom.parentNode, false);
18803     },
18804     
18805     onViewScroll : function(e, t){
18806         
18807         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){
18808             return;
18809         }
18810         
18811         this.hasQuery = true;
18812         
18813         this.loading = this.list.select('.loading', true).first();
18814         
18815         if(this.loading === null){
18816             this.list.createChild({
18817                 tag: 'div',
18818                 cls: 'loading roo-select2-more-results roo-select2-active',
18819                 html: 'Loading more results...'
18820             });
18821             
18822             this.loading = this.list.select('.loading', true).first();
18823             
18824             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18825             
18826             this.loading.hide();
18827         }
18828         
18829         this.loading.show();
18830         
18831         var _combo = this;
18832         
18833         this.page++;
18834         this.loadNext = true;
18835         
18836         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18837         
18838         return;
18839     },
18840     
18841     addItem : function(o)
18842     {   
18843         var dv = ''; // display value
18844         
18845         if (this.displayField) {
18846             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18847         } else {
18848             // this is an error condition!!!
18849             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18850         }
18851         
18852         if(!dv.length){
18853             return;
18854         }
18855         
18856         var choice = this.choices.createChild({
18857             tag: 'li',
18858             cls: 'roo-select2-search-choice',
18859             cn: [
18860                 {
18861                     tag: 'div',
18862                     html: dv
18863                 },
18864                 {
18865                     tag: 'a',
18866                     href: '#',
18867                     cls: 'roo-select2-search-choice-close fa fa-times',
18868                     tabindex: '-1'
18869                 }
18870             ]
18871             
18872         }, this.searchField);
18873         
18874         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18875         
18876         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18877         
18878         this.item.push(o);
18879         
18880         this.lastData = o;
18881         
18882         this.syncValue();
18883         
18884         this.inputEl().dom.value = '';
18885         
18886         this.validate();
18887     },
18888     
18889     onRemoveItem : function(e, _self, o)
18890     {
18891         e.preventDefault();
18892         
18893         this.lastItem = Roo.apply([], this.item);
18894         
18895         var index = this.item.indexOf(o.data) * 1;
18896         
18897         if( index < 0){
18898             Roo.log('not this item?!');
18899             return;
18900         }
18901         
18902         this.item.splice(index, 1);
18903         o.item.remove();
18904         
18905         this.syncValue();
18906         
18907         this.fireEvent('remove', this, e);
18908         
18909         this.validate();
18910         
18911     },
18912     
18913     syncValue : function()
18914     {
18915         if(!this.item.length){
18916             this.clearValue();
18917             return;
18918         }
18919             
18920         var value = [];
18921         var _this = this;
18922         Roo.each(this.item, function(i){
18923             if(_this.valueField){
18924                 value.push(i[_this.valueField]);
18925                 return;
18926             }
18927
18928             value.push(i);
18929         });
18930
18931         this.value = value.join(',');
18932
18933         if(this.hiddenField){
18934             this.hiddenField.dom.value = this.value;
18935         }
18936         
18937         this.store.fireEvent("datachanged", this.store);
18938         
18939         this.validate();
18940     },
18941     
18942     clearItem : function()
18943     {
18944         if(!this.multiple){
18945             return;
18946         }
18947         
18948         this.item = [];
18949         
18950         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18951            c.remove();
18952         });
18953         
18954         this.syncValue();
18955         
18956         this.validate();
18957         
18958         if(this.tickable && !Roo.isTouch){
18959             this.view.refresh();
18960         }
18961     },
18962     
18963     inputEl: function ()
18964     {
18965         if(Roo.isIOS && this.useNativeIOS){
18966             return this.el.select('select.roo-ios-select', true).first();
18967         }
18968         
18969         if(Roo.isTouch && this.mobileTouchView){
18970             return this.el.select('input.form-control',true).first();
18971         }
18972         
18973         if(this.tickable){
18974             return this.searchField;
18975         }
18976         
18977         return this.el.select('input.form-control',true).first();
18978     },
18979     
18980     onTickableFooterButtonClick : function(e, btn, el)
18981     {
18982         e.preventDefault();
18983         
18984         this.lastItem = Roo.apply([], this.item);
18985         
18986         if(btn && btn.name == 'cancel'){
18987             this.tickItems = Roo.apply([], this.item);
18988             this.collapse();
18989             return;
18990         }
18991         
18992         this.clearItem();
18993         
18994         var _this = this;
18995         
18996         Roo.each(this.tickItems, function(o){
18997             _this.addItem(o);
18998         });
18999         
19000         this.collapse();
19001         
19002     },
19003     
19004     validate : function()
19005     {
19006         if(this.getVisibilityEl().hasClass('hidden')){
19007             return true;
19008         }
19009         
19010         var v = this.getRawValue();
19011         
19012         if(this.multiple){
19013             v = this.getValue();
19014         }
19015         
19016         if(this.disabled || this.allowBlank || v.length){
19017             this.markValid();
19018             return true;
19019         }
19020         
19021         this.markInvalid();
19022         return false;
19023     },
19024     
19025     tickableInputEl : function()
19026     {
19027         if(!this.tickable || !this.editable){
19028             return this.inputEl();
19029         }
19030         
19031         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19032     },
19033     
19034     
19035     getAutoCreateTouchView : function()
19036     {
19037         var id = Roo.id();
19038         
19039         var cfg = {
19040             cls: 'form-group' //input-group
19041         };
19042         
19043         var input =  {
19044             tag: 'input',
19045             id : id,
19046             type : this.inputType,
19047             cls : 'form-control x-combo-noedit',
19048             autocomplete: 'new-password',
19049             placeholder : this.placeholder || '',
19050             readonly : true
19051         };
19052         
19053         if (this.name) {
19054             input.name = this.name;
19055         }
19056         
19057         if (this.size) {
19058             input.cls += ' input-' + this.size;
19059         }
19060         
19061         if (this.disabled) {
19062             input.disabled = true;
19063         }
19064         
19065         var inputblock = {
19066             cls : 'roo-combobox-wrap',
19067             cn : [
19068                 input
19069             ]
19070         };
19071         
19072         if(this.before){
19073             inputblock.cls += ' input-group';
19074             
19075             inputblock.cn.unshift({
19076                 tag :'span',
19077                 cls : 'input-group-addon input-group-prepend input-group-text',
19078                 html : this.before
19079             });
19080         }
19081         
19082         if(this.removable && !this.multiple){
19083             inputblock.cls += ' roo-removable';
19084             
19085             inputblock.cn.push({
19086                 tag: 'button',
19087                 html : 'x',
19088                 cls : 'roo-combo-removable-btn close'
19089             });
19090         }
19091
19092         if(this.hasFeedback && !this.allowBlank){
19093             
19094             inputblock.cls += ' has-feedback';
19095             
19096             inputblock.cn.push({
19097                 tag: 'span',
19098                 cls: 'glyphicon form-control-feedback'
19099             });
19100             
19101         }
19102         
19103         if (this.after) {
19104             
19105             inputblock.cls += (this.before) ? '' : ' input-group';
19106             
19107             inputblock.cn.push({
19108                 tag :'span',
19109                 cls : 'input-group-addon input-group-append input-group-text',
19110                 html : this.after
19111             });
19112         }
19113
19114         
19115         var ibwrap = inputblock;
19116         
19117         if(this.multiple){
19118             ibwrap = {
19119                 tag: 'ul',
19120                 cls: 'roo-select2-choices',
19121                 cn:[
19122                     {
19123                         tag: 'li',
19124                         cls: 'roo-select2-search-field',
19125                         cn: [
19126
19127                             inputblock
19128                         ]
19129                     }
19130                 ]
19131             };
19132         
19133             
19134         }
19135         
19136         var combobox = {
19137             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19138             cn: [
19139                 {
19140                     tag: 'input',
19141                     type : 'hidden',
19142                     cls: 'form-hidden-field'
19143                 },
19144                 ibwrap
19145             ]
19146         };
19147         
19148         if(!this.multiple && this.showToggleBtn){
19149             
19150             var caret = {
19151                 cls: 'caret'
19152             };
19153             
19154             if (this.caret != false) {
19155                 caret = {
19156                      tag: 'i',
19157                      cls: 'fa fa-' + this.caret
19158                 };
19159                 
19160             }
19161             
19162             combobox.cn.push({
19163                 tag :'span',
19164                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19165                 cn : [
19166                     Roo.bootstrap.version == 3 ? caret : '',
19167                     {
19168                         tag: 'span',
19169                         cls: 'combobox-clear',
19170                         cn  : [
19171                             {
19172                                 tag : 'i',
19173                                 cls: 'icon-remove'
19174                             }
19175                         ]
19176                     }
19177                 ]
19178
19179             })
19180         }
19181         
19182         if(this.multiple){
19183             combobox.cls += ' roo-select2-container-multi';
19184         }
19185         
19186         var required =  this.allowBlank ?  {
19187                     tag : 'i',
19188                     style: 'display: none'
19189                 } : {
19190                    tag : 'i',
19191                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19192                    tooltip : 'This field is required'
19193                 };
19194         
19195         var align = this.labelAlign || this.parentLabelAlign();
19196         
19197         if (align ==='left' && this.fieldLabel.length) {
19198
19199             cfg.cn = [
19200                 required,
19201                 {
19202                     tag: 'label',
19203                     cls : 'control-label col-form-label',
19204                     html : this.fieldLabel
19205
19206                 },
19207                 {
19208                     cls : 'roo-combobox-wrap ', 
19209                     cn: [
19210                         combobox
19211                     ]
19212                 }
19213             ];
19214             
19215             var labelCfg = cfg.cn[1];
19216             var contentCfg = cfg.cn[2];
19217             
19218
19219             if(this.indicatorpos == 'right'){
19220                 cfg.cn = [
19221                     {
19222                         tag: 'label',
19223                         'for' :  id,
19224                         cls : 'control-label col-form-label',
19225                         cn : [
19226                             {
19227                                 tag : 'span',
19228                                 html : this.fieldLabel
19229                             },
19230                             required
19231                         ]
19232                     },
19233                     {
19234                         cls : "roo-combobox-wrap ",
19235                         cn: [
19236                             combobox
19237                         ]
19238                     }
19239
19240                 ];
19241                 
19242                 labelCfg = cfg.cn[0];
19243                 contentCfg = cfg.cn[1];
19244             }
19245             
19246            
19247             
19248             if(this.labelWidth > 12){
19249                 labelCfg.style = "width: " + this.labelWidth + 'px';
19250             }
19251            
19252             if(this.labelWidth < 13 && this.labelmd == 0){
19253                 this.labelmd = this.labelWidth;
19254             }
19255             
19256             if(this.labellg > 0){
19257                 labelCfg.cls += ' col-lg-' + this.labellg;
19258                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19259             }
19260             
19261             if(this.labelmd > 0){
19262                 labelCfg.cls += ' col-md-' + this.labelmd;
19263                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19264             }
19265             
19266             if(this.labelsm > 0){
19267                 labelCfg.cls += ' col-sm-' + this.labelsm;
19268                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19269             }
19270             
19271             if(this.labelxs > 0){
19272                 labelCfg.cls += ' col-xs-' + this.labelxs;
19273                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19274             }
19275                 
19276                 
19277         } else if ( this.fieldLabel.length) {
19278             cfg.cn = [
19279                required,
19280                 {
19281                     tag: 'label',
19282                     cls : 'control-label',
19283                     html : this.fieldLabel
19284
19285                 },
19286                 {
19287                     cls : '', 
19288                     cn: [
19289                         combobox
19290                     ]
19291                 }
19292             ];
19293             
19294             if(this.indicatorpos == 'right'){
19295                 cfg.cn = [
19296                     {
19297                         tag: 'label',
19298                         cls : 'control-label',
19299                         html : this.fieldLabel,
19300                         cn : [
19301                             required
19302                         ]
19303                     },
19304                     {
19305                         cls : '', 
19306                         cn: [
19307                             combobox
19308                         ]
19309                     }
19310                 ];
19311             }
19312         } else {
19313             cfg.cn = combobox;    
19314         }
19315         
19316         
19317         var settings = this;
19318         
19319         ['xs','sm','md','lg'].map(function(size){
19320             if (settings[size]) {
19321                 cfg.cls += ' col-' + size + '-' + settings[size];
19322             }
19323         });
19324         
19325         return cfg;
19326     },
19327     
19328     initTouchView : function()
19329     {
19330         this.renderTouchView();
19331         
19332         this.touchViewEl.on('scroll', function(){
19333             this.el.dom.scrollTop = 0;
19334         }, this);
19335         
19336         this.originalValue = this.getValue();
19337         
19338         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19339         
19340         this.inputEl().on("click", this.showTouchView, this);
19341         if (this.triggerEl) {
19342             this.triggerEl.on("click", this.showTouchView, this);
19343         }
19344         
19345         
19346         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19347         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19348         
19349         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19350         
19351         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19352         this.store.on('load', this.onTouchViewLoad, this);
19353         this.store.on('loadexception', this.onTouchViewLoadException, this);
19354         
19355         if(this.hiddenName){
19356             
19357             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19358             
19359             this.hiddenField.dom.value =
19360                 this.hiddenValue !== undefined ? this.hiddenValue :
19361                 this.value !== undefined ? this.value : '';
19362         
19363             this.el.dom.removeAttribute('name');
19364             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19365         }
19366         
19367         if(this.multiple){
19368             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19369             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19370         }
19371         
19372         if(this.removable && !this.multiple){
19373             var close = this.closeTriggerEl();
19374             if(close){
19375                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19376                 close.on('click', this.removeBtnClick, this, close);
19377             }
19378         }
19379         /*
19380          * fix the bug in Safari iOS8
19381          */
19382         this.inputEl().on("focus", function(e){
19383             document.activeElement.blur();
19384         }, this);
19385         
19386         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19387         
19388         return;
19389         
19390         
19391     },
19392     
19393     renderTouchView : function()
19394     {
19395         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19396         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19397         
19398         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19399         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19400         
19401         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19402         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19403         this.touchViewBodyEl.setStyle('overflow', 'auto');
19404         
19405         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19406         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19407         
19408         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19409         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19410         
19411     },
19412     
19413     showTouchView : function()
19414     {
19415         if(this.disabled){
19416             return;
19417         }
19418         
19419         this.touchViewHeaderEl.hide();
19420
19421         if(this.modalTitle.length){
19422             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19423             this.touchViewHeaderEl.show();
19424         }
19425
19426         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19427         this.touchViewEl.show();
19428
19429         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19430         
19431         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19432         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19433
19434         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19435
19436         if(this.modalTitle.length){
19437             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19438         }
19439         
19440         this.touchViewBodyEl.setHeight(bodyHeight);
19441
19442         if(this.animate){
19443             var _this = this;
19444             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19445         }else{
19446             this.touchViewEl.addClass(['in','show']);
19447         }
19448         
19449         if(this._touchViewMask){
19450             Roo.get(document.body).addClass("x-body-masked");
19451             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19452             this._touchViewMask.setStyle('z-index', 10000);
19453             this._touchViewMask.addClass('show');
19454         }
19455         
19456         this.doTouchViewQuery();
19457         
19458     },
19459     
19460     hideTouchView : function()
19461     {
19462         this.touchViewEl.removeClass(['in','show']);
19463
19464         if(this.animate){
19465             var _this = this;
19466             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19467         }else{
19468             this.touchViewEl.setStyle('display', 'none');
19469         }
19470         
19471         if(this._touchViewMask){
19472             this._touchViewMask.removeClass('show');
19473             Roo.get(document.body).removeClass("x-body-masked");
19474         }
19475     },
19476     
19477     setTouchViewValue : function()
19478     {
19479         if(this.multiple){
19480             this.clearItem();
19481         
19482             var _this = this;
19483
19484             Roo.each(this.tickItems, function(o){
19485                 this.addItem(o);
19486             }, this);
19487         }
19488         
19489         this.hideTouchView();
19490     },
19491     
19492     doTouchViewQuery : function()
19493     {
19494         var qe = {
19495             query: '',
19496             forceAll: true,
19497             combo: this,
19498             cancel:false
19499         };
19500         
19501         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19502             return false;
19503         }
19504         
19505         if(!this.alwaysQuery || this.mode == 'local'){
19506             this.onTouchViewLoad();
19507             return;
19508         }
19509         
19510         this.store.load();
19511     },
19512     
19513     onTouchViewBeforeLoad : function(combo,opts)
19514     {
19515         return;
19516     },
19517
19518     // private
19519     onTouchViewLoad : function()
19520     {
19521         if(this.store.getCount() < 1){
19522             this.onTouchViewEmptyResults();
19523             return;
19524         }
19525         
19526         this.clearTouchView();
19527         
19528         var rawValue = this.getRawValue();
19529         
19530         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19531         
19532         this.tickItems = [];
19533         
19534         this.store.data.each(function(d, rowIndex){
19535             var row = this.touchViewListGroup.createChild(template);
19536             
19537             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19538                 row.addClass(d.data.cls);
19539             }
19540             
19541             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19542                 var cfg = {
19543                     data : d.data,
19544                     html : d.data[this.displayField]
19545                 };
19546                 
19547                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19548                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19549                 }
19550             }
19551             row.removeClass('selected');
19552             if(!this.multiple && this.valueField &&
19553                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19554             {
19555                 // radio buttons..
19556                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19557                 row.addClass('selected');
19558             }
19559             
19560             if(this.multiple && this.valueField &&
19561                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19562             {
19563                 
19564                 // checkboxes...
19565                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19566                 this.tickItems.push(d.data);
19567             }
19568             
19569             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19570             
19571         }, this);
19572         
19573         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19574         
19575         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19576
19577         if(this.modalTitle.length){
19578             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19579         }
19580
19581         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19582         
19583         if(this.mobile_restrict_height && listHeight < bodyHeight){
19584             this.touchViewBodyEl.setHeight(listHeight);
19585         }
19586         
19587         var _this = this;
19588         
19589         if(firstChecked && listHeight > bodyHeight){
19590             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19591         }
19592         
19593     },
19594     
19595     onTouchViewLoadException : function()
19596     {
19597         this.hideTouchView();
19598     },
19599     
19600     onTouchViewEmptyResults : function()
19601     {
19602         this.clearTouchView();
19603         
19604         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19605         
19606         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19607         
19608     },
19609     
19610     clearTouchView : function()
19611     {
19612         this.touchViewListGroup.dom.innerHTML = '';
19613     },
19614     
19615     onTouchViewClick : function(e, el, o)
19616     {
19617         e.preventDefault();
19618         
19619         var row = o.row;
19620         var rowIndex = o.rowIndex;
19621         
19622         var r = this.store.getAt(rowIndex);
19623         
19624         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19625             
19626             if(!this.multiple){
19627                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19628                     c.dom.removeAttribute('checked');
19629                 }, this);
19630
19631                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19632
19633                 this.setFromData(r.data);
19634
19635                 var close = this.closeTriggerEl();
19636
19637                 if(close){
19638                     close.show();
19639                 }
19640
19641                 this.hideTouchView();
19642
19643                 this.fireEvent('select', this, r, rowIndex);
19644
19645                 return;
19646             }
19647
19648             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19649                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19650                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19651                 return;
19652             }
19653
19654             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19655             this.addItem(r.data);
19656             this.tickItems.push(r.data);
19657         }
19658     },
19659     
19660     getAutoCreateNativeIOS : function()
19661     {
19662         var cfg = {
19663             cls: 'form-group' //input-group,
19664         };
19665         
19666         var combobox =  {
19667             tag: 'select',
19668             cls : 'roo-ios-select'
19669         };
19670         
19671         if (this.name) {
19672             combobox.name = this.name;
19673         }
19674         
19675         if (this.disabled) {
19676             combobox.disabled = true;
19677         }
19678         
19679         var settings = this;
19680         
19681         ['xs','sm','md','lg'].map(function(size){
19682             if (settings[size]) {
19683                 cfg.cls += ' col-' + size + '-' + settings[size];
19684             }
19685         });
19686         
19687         cfg.cn = combobox;
19688         
19689         return cfg;
19690         
19691     },
19692     
19693     initIOSView : function()
19694     {
19695         this.store.on('load', this.onIOSViewLoad, this);
19696         
19697         return;
19698     },
19699     
19700     onIOSViewLoad : function()
19701     {
19702         if(this.store.getCount() < 1){
19703             return;
19704         }
19705         
19706         this.clearIOSView();
19707         
19708         if(this.allowBlank) {
19709             
19710             var default_text = '-- SELECT --';
19711             
19712             if(this.placeholder.length){
19713                 default_text = this.placeholder;
19714             }
19715             
19716             if(this.emptyTitle.length){
19717                 default_text += ' - ' + this.emptyTitle + ' -';
19718             }
19719             
19720             var opt = this.inputEl().createChild({
19721                 tag: 'option',
19722                 value : 0,
19723                 html : default_text
19724             });
19725             
19726             var o = {};
19727             o[this.valueField] = 0;
19728             o[this.displayField] = default_text;
19729             
19730             this.ios_options.push({
19731                 data : o,
19732                 el : opt
19733             });
19734             
19735         }
19736         
19737         this.store.data.each(function(d, rowIndex){
19738             
19739             var html = '';
19740             
19741             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19742                 html = d.data[this.displayField];
19743             }
19744             
19745             var value = '';
19746             
19747             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19748                 value = d.data[this.valueField];
19749             }
19750             
19751             var option = {
19752                 tag: 'option',
19753                 value : value,
19754                 html : html
19755             };
19756             
19757             if(this.value == d.data[this.valueField]){
19758                 option['selected'] = true;
19759             }
19760             
19761             var opt = this.inputEl().createChild(option);
19762             
19763             this.ios_options.push({
19764                 data : d.data,
19765                 el : opt
19766             });
19767             
19768         }, this);
19769         
19770         this.inputEl().on('change', function(){
19771            this.fireEvent('select', this);
19772         }, this);
19773         
19774     },
19775     
19776     clearIOSView: function()
19777     {
19778         this.inputEl().dom.innerHTML = '';
19779         
19780         this.ios_options = [];
19781     },
19782     
19783     setIOSValue: function(v)
19784     {
19785         this.value = v;
19786         
19787         if(!this.ios_options){
19788             return;
19789         }
19790         
19791         Roo.each(this.ios_options, function(opts){
19792            
19793            opts.el.dom.removeAttribute('selected');
19794            
19795            if(opts.data[this.valueField] != v){
19796                return;
19797            }
19798            
19799            opts.el.dom.setAttribute('selected', true);
19800            
19801         }, this);
19802     }
19803
19804     /** 
19805     * @cfg {Boolean} grow 
19806     * @hide 
19807     */
19808     /** 
19809     * @cfg {Number} growMin 
19810     * @hide 
19811     */
19812     /** 
19813     * @cfg {Number} growMax 
19814     * @hide 
19815     */
19816     /**
19817      * @hide
19818      * @method autoSize
19819      */
19820 });
19821
19822 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19823     
19824     header : {
19825         tag: 'div',
19826         cls: 'modal-header',
19827         cn: [
19828             {
19829                 tag: 'h4',
19830                 cls: 'modal-title'
19831             }
19832         ]
19833     },
19834     
19835     body : {
19836         tag: 'div',
19837         cls: 'modal-body',
19838         cn: [
19839             {
19840                 tag: 'ul',
19841                 cls: 'list-group'
19842             }
19843         ]
19844     },
19845     
19846     listItemRadio : {
19847         tag: 'li',
19848         cls: 'list-group-item',
19849         cn: [
19850             {
19851                 tag: 'span',
19852                 cls: 'roo-combobox-list-group-item-value'
19853             },
19854             {
19855                 tag: 'div',
19856                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19857                 cn: [
19858                     {
19859                         tag: 'input',
19860                         type: 'radio'
19861                     },
19862                     {
19863                         tag: 'label'
19864                     }
19865                 ]
19866             }
19867         ]
19868     },
19869     
19870     listItemCheckbox : {
19871         tag: 'li',
19872         cls: 'list-group-item',
19873         cn: [
19874             {
19875                 tag: 'span',
19876                 cls: 'roo-combobox-list-group-item-value'
19877             },
19878             {
19879                 tag: 'div',
19880                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19881                 cn: [
19882                     {
19883                         tag: 'input',
19884                         type: 'checkbox'
19885                     },
19886                     {
19887                         tag: 'label'
19888                     }
19889                 ]
19890             }
19891         ]
19892     },
19893     
19894     emptyResult : {
19895         tag: 'div',
19896         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19897     },
19898     
19899     footer : {
19900         tag: 'div',
19901         cls: 'modal-footer',
19902         cn: [
19903             {
19904                 tag: 'div',
19905                 cls: 'row',
19906                 cn: [
19907                     {
19908                         tag: 'div',
19909                         cls: 'col-xs-6 text-left',
19910                         cn: {
19911                             tag: 'button',
19912                             cls: 'btn btn-danger roo-touch-view-cancel',
19913                             html: 'Cancel'
19914                         }
19915                     },
19916                     {
19917                         tag: 'div',
19918                         cls: 'col-xs-6 text-right',
19919                         cn: {
19920                             tag: 'button',
19921                             cls: 'btn btn-success roo-touch-view-ok',
19922                             html: 'OK'
19923                         }
19924                     }
19925                 ]
19926             }
19927         ]
19928         
19929     }
19930 });
19931
19932 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19933     
19934     touchViewTemplate : {
19935         tag: 'div',
19936         cls: 'modal fade roo-combobox-touch-view',
19937         cn: [
19938             {
19939                 tag: 'div',
19940                 cls: 'modal-dialog',
19941                 style : 'position:fixed', // we have to fix position....
19942                 cn: [
19943                     {
19944                         tag: 'div',
19945                         cls: 'modal-content',
19946                         cn: [
19947                             Roo.bootstrap.form.ComboBox.header,
19948                             Roo.bootstrap.form.ComboBox.body,
19949                             Roo.bootstrap.form.ComboBox.footer
19950                         ]
19951                     }
19952                 ]
19953             }
19954         ]
19955     }
19956 });/*
19957  * Based on:
19958  * Ext JS Library 1.1.1
19959  * Copyright(c) 2006-2007, Ext JS, LLC.
19960  *
19961  * Originally Released Under LGPL - original licence link has changed is not relivant.
19962  *
19963  * Fork - LGPL
19964  * <script type="text/javascript">
19965  */
19966
19967 /**
19968  * @class Roo.View
19969  * @extends Roo.util.Observable
19970  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19971  * This class also supports single and multi selection modes. <br>
19972  * Create a data model bound view:
19973  <pre><code>
19974  var store = new Roo.data.Store(...);
19975
19976  var view = new Roo.View({
19977     el : "my-element",
19978     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19979  
19980     singleSelect: true,
19981     selectedClass: "ydataview-selected",
19982     store: store
19983  });
19984
19985  // listen for node click?
19986  view.on("click", function(vw, index, node, e){
19987  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19988  });
19989
19990  // load XML data
19991  dataModel.load("foobar.xml");
19992  </code></pre>
19993  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19994  * <br><br>
19995  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19996  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19997  * 
19998  * Note: old style constructor is still suported (container, template, config)
19999  * 
20000  * @constructor
20001  * Create a new View
20002  * @param {Object} config The config object
20003  * 
20004  */
20005 Roo.View = function(config, depreciated_tpl, depreciated_config){
20006     
20007     this.parent = false;
20008     
20009     if (typeof(depreciated_tpl) == 'undefined') {
20010         // new way.. - universal constructor.
20011         Roo.apply(this, config);
20012         this.el  = Roo.get(this.el);
20013     } else {
20014         // old format..
20015         this.el  = Roo.get(config);
20016         this.tpl = depreciated_tpl;
20017         Roo.apply(this, depreciated_config);
20018     }
20019     this.wrapEl  = this.el.wrap().wrap();
20020     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20021     
20022     
20023     if(typeof(this.tpl) == "string"){
20024         this.tpl = new Roo.Template(this.tpl);
20025     } else {
20026         // support xtype ctors..
20027         this.tpl = new Roo.factory(this.tpl, Roo);
20028     }
20029     
20030     
20031     this.tpl.compile();
20032     
20033     /** @private */
20034     this.addEvents({
20035         /**
20036          * @event beforeclick
20037          * Fires before a click is processed. Returns false to cancel the default action.
20038          * @param {Roo.View} this
20039          * @param {Number} index The index of the target node
20040          * @param {HTMLElement} node The target node
20041          * @param {Roo.EventObject} e The raw event object
20042          */
20043             "beforeclick" : true,
20044         /**
20045          * @event click
20046          * Fires when a template node is clicked.
20047          * @param {Roo.View} this
20048          * @param {Number} index The index of the target node
20049          * @param {HTMLElement} node The target node
20050          * @param {Roo.EventObject} e The raw event object
20051          */
20052             "click" : true,
20053         /**
20054          * @event dblclick
20055          * Fires when a template node is double clicked.
20056          * @param {Roo.View} this
20057          * @param {Number} index The index of the target node
20058          * @param {HTMLElement} node The target node
20059          * @param {Roo.EventObject} e The raw event object
20060          */
20061             "dblclick" : true,
20062         /**
20063          * @event contextmenu
20064          * Fires when a template node is right clicked.
20065          * @param {Roo.View} this
20066          * @param {Number} index The index of the target node
20067          * @param {HTMLElement} node The target node
20068          * @param {Roo.EventObject} e The raw event object
20069          */
20070             "contextmenu" : true,
20071         /**
20072          * @event selectionchange
20073          * Fires when the selected nodes change.
20074          * @param {Roo.View} this
20075          * @param {Array} selections Array of the selected nodes
20076          */
20077             "selectionchange" : true,
20078     
20079         /**
20080          * @event beforeselect
20081          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20082          * @param {Roo.View} this
20083          * @param {HTMLElement} node The node to be selected
20084          * @param {Array} selections Array of currently selected nodes
20085          */
20086             "beforeselect" : true,
20087         /**
20088          * @event preparedata
20089          * Fires on every row to render, to allow you to change the data.
20090          * @param {Roo.View} this
20091          * @param {Object} data to be rendered (change this)
20092          */
20093           "preparedata" : true
20094           
20095           
20096         });
20097
20098
20099
20100     this.el.on({
20101         "click": this.onClick,
20102         "dblclick": this.onDblClick,
20103         "contextmenu": this.onContextMenu,
20104         scope:this
20105     });
20106
20107     this.selections = [];
20108     this.nodes = [];
20109     this.cmp = new Roo.CompositeElementLite([]);
20110     if(this.store){
20111         this.store = Roo.factory(this.store, Roo.data);
20112         this.setStore(this.store, true);
20113     }
20114     
20115     if ( this.footer && this.footer.xtype) {
20116            
20117          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20118         
20119         this.footer.dataSource = this.store;
20120         this.footer.container = fctr;
20121         this.footer = Roo.factory(this.footer, Roo);
20122         fctr.insertFirst(this.el);
20123         
20124         // this is a bit insane - as the paging toolbar seems to detach the el..
20125 //        dom.parentNode.parentNode.parentNode
20126          // they get detached?
20127     }
20128     
20129     
20130     Roo.View.superclass.constructor.call(this);
20131     
20132     
20133 };
20134
20135 Roo.extend(Roo.View, Roo.util.Observable, {
20136     
20137      /**
20138      * @cfg {Roo.data.Store} store Data store to load data from.
20139      */
20140     store : false,
20141     
20142     /**
20143      * @cfg {String|Roo.Element} el The container element.
20144      */
20145     el : '',
20146     
20147     /**
20148      * @cfg {String|Roo.Template} tpl The template used by this View 
20149      */
20150     tpl : false,
20151     /**
20152      * @cfg {String} dataName the named area of the template to use as the data area
20153      *                          Works with domtemplates roo-name="name"
20154      */
20155     dataName: false,
20156     /**
20157      * @cfg {String} selectedClass The css class to add to selected nodes
20158      */
20159     selectedClass : "x-view-selected",
20160      /**
20161      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20162      */
20163     emptyText : "",
20164     
20165     /**
20166      * @cfg {String} text to display on mask (default Loading)
20167      */
20168     mask : false,
20169     /**
20170      * @cfg {Boolean} multiSelect Allow multiple selection
20171      */
20172     multiSelect : false,
20173     /**
20174      * @cfg {Boolean} singleSelect Allow single selection
20175      */
20176     singleSelect:  false,
20177     
20178     /**
20179      * @cfg {Boolean} toggleSelect - selecting 
20180      */
20181     toggleSelect : false,
20182     
20183     /**
20184      * @cfg {Boolean} tickable - selecting 
20185      */
20186     tickable : false,
20187     
20188     /**
20189      * Returns the element this view is bound to.
20190      * @return {Roo.Element}
20191      */
20192     getEl : function(){
20193         return this.wrapEl;
20194     },
20195     
20196     
20197
20198     /**
20199      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20200      */
20201     refresh : function(){
20202         //Roo.log('refresh');
20203         var t = this.tpl;
20204         
20205         // if we are using something like 'domtemplate', then
20206         // the what gets used is:
20207         // t.applySubtemplate(NAME, data, wrapping data..)
20208         // the outer template then get' applied with
20209         //     the store 'extra data'
20210         // and the body get's added to the
20211         //      roo-name="data" node?
20212         //      <span class='roo-tpl-{name}'></span> ?????
20213         
20214         
20215         
20216         this.clearSelections();
20217         this.el.update("");
20218         var html = [];
20219         var records = this.store.getRange();
20220         if(records.length < 1) {
20221             
20222             // is this valid??  = should it render a template??
20223             
20224             this.el.update(this.emptyText);
20225             return;
20226         }
20227         var el = this.el;
20228         if (this.dataName) {
20229             this.el.update(t.apply(this.store.meta)); //????
20230             el = this.el.child('.roo-tpl-' + this.dataName);
20231         }
20232         
20233         for(var i = 0, len = records.length; i < len; i++){
20234             var data = this.prepareData(records[i].data, i, records[i]);
20235             this.fireEvent("preparedata", this, data, i, records[i]);
20236             
20237             var d = Roo.apply({}, data);
20238             
20239             if(this.tickable){
20240                 Roo.apply(d, {'roo-id' : Roo.id()});
20241                 
20242                 var _this = this;
20243             
20244                 Roo.each(this.parent.item, function(item){
20245                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20246                         return;
20247                     }
20248                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20249                 });
20250             }
20251             
20252             html[html.length] = Roo.util.Format.trim(
20253                 this.dataName ?
20254                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20255                     t.apply(d)
20256             );
20257         }
20258         
20259         
20260         
20261         el.update(html.join(""));
20262         this.nodes = el.dom.childNodes;
20263         this.updateIndexes(0);
20264     },
20265     
20266
20267     /**
20268      * Function to override to reformat the data that is sent to
20269      * the template for each node.
20270      * DEPRICATED - use the preparedata event handler.
20271      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20272      * a JSON object for an UpdateManager bound view).
20273      */
20274     prepareData : function(data, index, record)
20275     {
20276         this.fireEvent("preparedata", this, data, index, record);
20277         return data;
20278     },
20279
20280     onUpdate : function(ds, record){
20281         // Roo.log('on update');   
20282         this.clearSelections();
20283         var index = this.store.indexOf(record);
20284         var n = this.nodes[index];
20285         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20286         n.parentNode.removeChild(n);
20287         this.updateIndexes(index, index);
20288     },
20289
20290     
20291     
20292 // --------- FIXME     
20293     onAdd : function(ds, records, index)
20294     {
20295         //Roo.log(['on Add', ds, records, index] );        
20296         this.clearSelections();
20297         if(this.nodes.length == 0){
20298             this.refresh();
20299             return;
20300         }
20301         var n = this.nodes[index];
20302         for(var i = 0, len = records.length; i < len; i++){
20303             var d = this.prepareData(records[i].data, i, records[i]);
20304             if(n){
20305                 this.tpl.insertBefore(n, d);
20306             }else{
20307                 
20308                 this.tpl.append(this.el, d);
20309             }
20310         }
20311         this.updateIndexes(index);
20312     },
20313
20314     onRemove : function(ds, record, index){
20315        // Roo.log('onRemove');
20316         this.clearSelections();
20317         var el = this.dataName  ?
20318             this.el.child('.roo-tpl-' + this.dataName) :
20319             this.el; 
20320         
20321         el.dom.removeChild(this.nodes[index]);
20322         this.updateIndexes(index);
20323     },
20324
20325     /**
20326      * Refresh an individual node.
20327      * @param {Number} index
20328      */
20329     refreshNode : function(index){
20330         this.onUpdate(this.store, this.store.getAt(index));
20331     },
20332
20333     updateIndexes : function(startIndex, endIndex){
20334         var ns = this.nodes;
20335         startIndex = startIndex || 0;
20336         endIndex = endIndex || ns.length - 1;
20337         for(var i = startIndex; i <= endIndex; i++){
20338             ns[i].nodeIndex = i;
20339         }
20340     },
20341
20342     /**
20343      * Changes the data store this view uses and refresh the view.
20344      * @param {Store} store
20345      */
20346     setStore : function(store, initial){
20347         if(!initial && this.store){
20348             this.store.un("datachanged", this.refresh);
20349             this.store.un("add", this.onAdd);
20350             this.store.un("remove", this.onRemove);
20351             this.store.un("update", this.onUpdate);
20352             this.store.un("clear", this.refresh);
20353             this.store.un("beforeload", this.onBeforeLoad);
20354             this.store.un("load", this.onLoad);
20355             this.store.un("loadexception", this.onLoad);
20356         }
20357         if(store){
20358           
20359             store.on("datachanged", this.refresh, this);
20360             store.on("add", this.onAdd, this);
20361             store.on("remove", this.onRemove, this);
20362             store.on("update", this.onUpdate, this);
20363             store.on("clear", this.refresh, this);
20364             store.on("beforeload", this.onBeforeLoad, this);
20365             store.on("load", this.onLoad, this);
20366             store.on("loadexception", this.onLoad, this);
20367         }
20368         
20369         if(store){
20370             this.refresh();
20371         }
20372     },
20373     /**
20374      * onbeforeLoad - masks the loading area.
20375      *
20376      */
20377     onBeforeLoad : function(store,opts)
20378     {
20379          //Roo.log('onBeforeLoad');   
20380         if (!opts.add) {
20381             this.el.update("");
20382         }
20383         this.el.mask(this.mask ? this.mask : "Loading" ); 
20384     },
20385     onLoad : function ()
20386     {
20387         this.el.unmask();
20388     },
20389     
20390
20391     /**
20392      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20393      * @param {HTMLElement} node
20394      * @return {HTMLElement} The template node
20395      */
20396     findItemFromChild : function(node){
20397         var el = this.dataName  ?
20398             this.el.child('.roo-tpl-' + this.dataName,true) :
20399             this.el.dom; 
20400         
20401         if(!node || node.parentNode == el){
20402                     return node;
20403             }
20404             var p = node.parentNode;
20405             while(p && p != el){
20406             if(p.parentNode == el){
20407                 return p;
20408             }
20409             p = p.parentNode;
20410         }
20411             return null;
20412     },
20413
20414     /** @ignore */
20415     onClick : function(e){
20416         var item = this.findItemFromChild(e.getTarget());
20417         if(item){
20418             var index = this.indexOf(item);
20419             if(this.onItemClick(item, index, e) !== false){
20420                 this.fireEvent("click", this, index, item, e);
20421             }
20422         }else{
20423             this.clearSelections();
20424         }
20425     },
20426
20427     /** @ignore */
20428     onContextMenu : function(e){
20429         var item = this.findItemFromChild(e.getTarget());
20430         if(item){
20431             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20432         }
20433     },
20434
20435     /** @ignore */
20436     onDblClick : function(e){
20437         var item = this.findItemFromChild(e.getTarget());
20438         if(item){
20439             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20440         }
20441     },
20442
20443     onItemClick : function(item, index, e)
20444     {
20445         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20446             return false;
20447         }
20448         if (this.toggleSelect) {
20449             var m = this.isSelected(item) ? 'unselect' : 'select';
20450             //Roo.log(m);
20451             var _t = this;
20452             _t[m](item, true, false);
20453             return true;
20454         }
20455         if(this.multiSelect || this.singleSelect){
20456             if(this.multiSelect && e.shiftKey && this.lastSelection){
20457                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20458             }else{
20459                 this.select(item, this.multiSelect && e.ctrlKey);
20460                 this.lastSelection = item;
20461             }
20462             
20463             if(!this.tickable){
20464                 e.preventDefault();
20465             }
20466             
20467         }
20468         return true;
20469     },
20470
20471     /**
20472      * Get the number of selected nodes.
20473      * @return {Number}
20474      */
20475     getSelectionCount : function(){
20476         return this.selections.length;
20477     },
20478
20479     /**
20480      * Get the currently selected nodes.
20481      * @return {Array} An array of HTMLElements
20482      */
20483     getSelectedNodes : function(){
20484         return this.selections;
20485     },
20486
20487     /**
20488      * Get the indexes of the selected nodes.
20489      * @return {Array}
20490      */
20491     getSelectedIndexes : function(){
20492         var indexes = [], s = this.selections;
20493         for(var i = 0, len = s.length; i < len; i++){
20494             indexes.push(s[i].nodeIndex);
20495         }
20496         return indexes;
20497     },
20498
20499     /**
20500      * Clear all selections
20501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20502      */
20503     clearSelections : function(suppressEvent){
20504         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20505             this.cmp.elements = this.selections;
20506             this.cmp.removeClass(this.selectedClass);
20507             this.selections = [];
20508             if(!suppressEvent){
20509                 this.fireEvent("selectionchange", this, this.selections);
20510             }
20511         }
20512     },
20513
20514     /**
20515      * Returns true if the passed node is selected
20516      * @param {HTMLElement/Number} node The node or node index
20517      * @return {Boolean}
20518      */
20519     isSelected : function(node){
20520         var s = this.selections;
20521         if(s.length < 1){
20522             return false;
20523         }
20524         node = this.getNode(node);
20525         return s.indexOf(node) !== -1;
20526     },
20527
20528     /**
20529      * Selects nodes.
20530      * @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
20531      * @param {Boolean} keepExisting (optional) true to keep existing selections
20532      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20533      */
20534     select : function(nodeInfo, keepExisting, suppressEvent){
20535         if(nodeInfo instanceof Array){
20536             if(!keepExisting){
20537                 this.clearSelections(true);
20538             }
20539             for(var i = 0, len = nodeInfo.length; i < len; i++){
20540                 this.select(nodeInfo[i], true, true);
20541             }
20542             return;
20543         } 
20544         var node = this.getNode(nodeInfo);
20545         if(!node || this.isSelected(node)){
20546             return; // already selected.
20547         }
20548         if(!keepExisting){
20549             this.clearSelections(true);
20550         }
20551         
20552         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20553             Roo.fly(node).addClass(this.selectedClass);
20554             this.selections.push(node);
20555             if(!suppressEvent){
20556                 this.fireEvent("selectionchange", this, this.selections);
20557             }
20558         }
20559         
20560         
20561     },
20562       /**
20563      * Unselects nodes.
20564      * @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
20565      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20567      */
20568     unselect : function(nodeInfo, keepExisting, suppressEvent)
20569     {
20570         if(nodeInfo instanceof Array){
20571             Roo.each(this.selections, function(s) {
20572                 this.unselect(s, nodeInfo);
20573             }, this);
20574             return;
20575         }
20576         var node = this.getNode(nodeInfo);
20577         if(!node || !this.isSelected(node)){
20578             //Roo.log("not selected");
20579             return; // not selected.
20580         }
20581         // fireevent???
20582         var ns = [];
20583         Roo.each(this.selections, function(s) {
20584             if (s == node ) {
20585                 Roo.fly(node).removeClass(this.selectedClass);
20586
20587                 return;
20588             }
20589             ns.push(s);
20590         },this);
20591         
20592         this.selections= ns;
20593         this.fireEvent("selectionchange", this, this.selections);
20594     },
20595
20596     /**
20597      * Gets a template node.
20598      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20599      * @return {HTMLElement} The node or null if it wasn't found
20600      */
20601     getNode : function(nodeInfo){
20602         if(typeof nodeInfo == "string"){
20603             return document.getElementById(nodeInfo);
20604         }else if(typeof nodeInfo == "number"){
20605             return this.nodes[nodeInfo];
20606         }
20607         return nodeInfo;
20608     },
20609
20610     /**
20611      * Gets a range template nodes.
20612      * @param {Number} startIndex
20613      * @param {Number} endIndex
20614      * @return {Array} An array of nodes
20615      */
20616     getNodes : function(start, end){
20617         var ns = this.nodes;
20618         start = start || 0;
20619         end = typeof end == "undefined" ? ns.length - 1 : end;
20620         var nodes = [];
20621         if(start <= end){
20622             for(var i = start; i <= end; i++){
20623                 nodes.push(ns[i]);
20624             }
20625         } else{
20626             for(var i = start; i >= end; i--){
20627                 nodes.push(ns[i]);
20628             }
20629         }
20630         return nodes;
20631     },
20632
20633     /**
20634      * Finds the index of the passed node
20635      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20636      * @return {Number} The index of the node or -1
20637      */
20638     indexOf : function(node){
20639         node = this.getNode(node);
20640         if(typeof node.nodeIndex == "number"){
20641             return node.nodeIndex;
20642         }
20643         var ns = this.nodes;
20644         for(var i = 0, len = ns.length; i < len; i++){
20645             if(ns[i] == node){
20646                 return i;
20647             }
20648         }
20649         return -1;
20650     }
20651 });
20652 /*
20653  * - LGPL
20654  *
20655  * based on jquery fullcalendar
20656  * 
20657  */
20658
20659 Roo.bootstrap = Roo.bootstrap || {};
20660 /**
20661  * @class Roo.bootstrap.Calendar
20662  * @extends Roo.bootstrap.Component
20663  * Bootstrap Calendar class
20664  * @cfg {Boolean} loadMask (true|false) default false
20665  * @cfg {Object} header generate the user specific header of the calendar, default false
20666
20667  * @constructor
20668  * Create a new Container
20669  * @param {Object} config The config object
20670  */
20671
20672
20673
20674 Roo.bootstrap.Calendar = function(config){
20675     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20676      this.addEvents({
20677         /**
20678              * @event select
20679              * Fires when a date is selected
20680              * @param {DatePicker} this
20681              * @param {Date} date The selected date
20682              */
20683         'select': true,
20684         /**
20685              * @event monthchange
20686              * Fires when the displayed month changes 
20687              * @param {DatePicker} this
20688              * @param {Date} date The selected month
20689              */
20690         'monthchange': true,
20691         /**
20692              * @event evententer
20693              * Fires when mouse over an event
20694              * @param {Calendar} this
20695              * @param {event} Event
20696              */
20697         'evententer': true,
20698         /**
20699              * @event eventleave
20700              * Fires when the mouse leaves an
20701              * @param {Calendar} this
20702              * @param {event}
20703              */
20704         'eventleave': true,
20705         /**
20706              * @event eventclick
20707              * Fires when the mouse click an
20708              * @param {Calendar} this
20709              * @param {event}
20710              */
20711         'eventclick': true
20712         
20713     });
20714
20715 };
20716
20717 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20718     
20719           /**
20720      * @cfg {Roo.data.Store} store
20721      * The data source for the calendar
20722      */
20723         store : false,
20724      /**
20725      * @cfg {Number} startDay
20726      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20727      */
20728     startDay : 0,
20729     
20730     loadMask : false,
20731     
20732     header : false,
20733       
20734     getAutoCreate : function(){
20735         
20736         
20737         var fc_button = function(name, corner, style, content ) {
20738             return Roo.apply({},{
20739                 tag : 'span',
20740                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20741                          (corner.length ?
20742                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20743                             ''
20744                         ),
20745                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20746                 unselectable: 'on'
20747             });
20748         };
20749         
20750         var header = {};
20751         
20752         if(!this.header){
20753             header = {
20754                 tag : 'table',
20755                 cls : 'fc-header',
20756                 style : 'width:100%',
20757                 cn : [
20758                     {
20759                         tag: 'tr',
20760                         cn : [
20761                             {
20762                                 tag : 'td',
20763                                 cls : 'fc-header-left',
20764                                 cn : [
20765                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20766                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20767                                     { tag: 'span', cls: 'fc-header-space' },
20768                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20769
20770
20771                                 ]
20772                             },
20773
20774                             {
20775                                 tag : 'td',
20776                                 cls : 'fc-header-center',
20777                                 cn : [
20778                                     {
20779                                         tag: 'span',
20780                                         cls: 'fc-header-title',
20781                                         cn : {
20782                                             tag: 'H2',
20783                                             html : 'month / year'
20784                                         }
20785                                     }
20786
20787                                 ]
20788                             },
20789                             {
20790                                 tag : 'td',
20791                                 cls : 'fc-header-right',
20792                                 cn : [
20793                               /*      fc_button('month', 'left', '', 'month' ),
20794                                     fc_button('week', '', '', 'week' ),
20795                                     fc_button('day', 'right', '', 'day' )
20796                                 */    
20797
20798                                 ]
20799                             }
20800
20801                         ]
20802                     }
20803                 ]
20804             };
20805         }
20806         
20807         header = this.header;
20808         
20809        
20810         var cal_heads = function() {
20811             var ret = [];
20812             // fixme - handle this.
20813             
20814             for (var i =0; i < Date.dayNames.length; i++) {
20815                 var d = Date.dayNames[i];
20816                 ret.push({
20817                     tag: 'th',
20818                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20819                     html : d.substring(0,3)
20820                 });
20821                 
20822             }
20823             ret[0].cls += ' fc-first';
20824             ret[6].cls += ' fc-last';
20825             return ret;
20826         };
20827         var cal_cell = function(n) {
20828             return  {
20829                 tag: 'td',
20830                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20831                 cn : [
20832                     {
20833                         cn : [
20834                             {
20835                                 cls: 'fc-day-number',
20836                                 html: 'D'
20837                             },
20838                             {
20839                                 cls: 'fc-day-content',
20840                              
20841                                 cn : [
20842                                      {
20843                                         style: 'position: relative;' // height: 17px;
20844                                     }
20845                                 ]
20846                             }
20847                             
20848                             
20849                         ]
20850                     }
20851                 ]
20852                 
20853             }
20854         };
20855         var cal_rows = function() {
20856             
20857             var ret = [];
20858             for (var r = 0; r < 6; r++) {
20859                 var row= {
20860                     tag : 'tr',
20861                     cls : 'fc-week',
20862                     cn : []
20863                 };
20864                 
20865                 for (var i =0; i < Date.dayNames.length; i++) {
20866                     var d = Date.dayNames[i];
20867                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20868
20869                 }
20870                 row.cn[0].cls+=' fc-first';
20871                 row.cn[0].cn[0].style = 'min-height:90px';
20872                 row.cn[6].cls+=' fc-last';
20873                 ret.push(row);
20874                 
20875             }
20876             ret[0].cls += ' fc-first';
20877             ret[4].cls += ' fc-prev-last';
20878             ret[5].cls += ' fc-last';
20879             return ret;
20880             
20881         };
20882         
20883         var cal_table = {
20884             tag: 'table',
20885             cls: 'fc-border-separate',
20886             style : 'width:100%',
20887             cellspacing  : 0,
20888             cn : [
20889                 { 
20890                     tag: 'thead',
20891                     cn : [
20892                         { 
20893                             tag: 'tr',
20894                             cls : 'fc-first fc-last',
20895                             cn : cal_heads()
20896                         }
20897                     ]
20898                 },
20899                 { 
20900                     tag: 'tbody',
20901                     cn : cal_rows()
20902                 }
20903                   
20904             ]
20905         };
20906          
20907          var cfg = {
20908             cls : 'fc fc-ltr',
20909             cn : [
20910                 header,
20911                 {
20912                     cls : 'fc-content',
20913                     style : "position: relative;",
20914                     cn : [
20915                         {
20916                             cls : 'fc-view fc-view-month fc-grid',
20917                             style : 'position: relative',
20918                             unselectable : 'on',
20919                             cn : [
20920                                 {
20921                                     cls : 'fc-event-container',
20922                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20923                                 },
20924                                 cal_table
20925                             ]
20926                         }
20927                     ]
20928     
20929                 }
20930            ] 
20931             
20932         };
20933         
20934          
20935         
20936         return cfg;
20937     },
20938     
20939     
20940     initEvents : function()
20941     {
20942         if(!this.store){
20943             throw "can not find store for calendar";
20944         }
20945         
20946         var mark = {
20947             tag: "div",
20948             cls:"x-dlg-mask",
20949             style: "text-align:center",
20950             cn: [
20951                 {
20952                     tag: "div",
20953                     style: "background-color:white;width:50%;margin:250 auto",
20954                     cn: [
20955                         {
20956                             tag: "img",
20957                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20958                         },
20959                         {
20960                             tag: "span",
20961                             html: "Loading"
20962                         }
20963                         
20964                     ]
20965                 }
20966             ]
20967         };
20968         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20969         
20970         var size = this.el.select('.fc-content', true).first().getSize();
20971         this.maskEl.setSize(size.width, size.height);
20972         this.maskEl.enableDisplayMode("block");
20973         if(!this.loadMask){
20974             this.maskEl.hide();
20975         }
20976         
20977         this.store = Roo.factory(this.store, Roo.data);
20978         this.store.on('load', this.onLoad, this);
20979         this.store.on('beforeload', this.onBeforeLoad, this);
20980         
20981         this.resize();
20982         
20983         this.cells = this.el.select('.fc-day',true);
20984         //Roo.log(this.cells);
20985         this.textNodes = this.el.query('.fc-day-number');
20986         this.cells.addClassOnOver('fc-state-hover');
20987         
20988         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20989         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20990         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20991         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20992         
20993         this.on('monthchange', this.onMonthChange, this);
20994         
20995         this.update(new Date().clearTime());
20996     },
20997     
20998     resize : function() {
20999         var sz  = this.el.getSize();
21000         
21001         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21002         this.el.select('.fc-day-content div',true).setHeight(34);
21003     },
21004     
21005     
21006     // private
21007     showPrevMonth : function(e){
21008         this.update(this.activeDate.add("mo", -1));
21009     },
21010     showToday : function(e){
21011         this.update(new Date().clearTime());
21012     },
21013     // private
21014     showNextMonth : function(e){
21015         this.update(this.activeDate.add("mo", 1));
21016     },
21017
21018     // private
21019     showPrevYear : function(){
21020         this.update(this.activeDate.add("y", -1));
21021     },
21022
21023     // private
21024     showNextYear : function(){
21025         this.update(this.activeDate.add("y", 1));
21026     },
21027
21028     
21029    // private
21030     update : function(date)
21031     {
21032         var vd = this.activeDate;
21033         this.activeDate = date;
21034 //        if(vd && this.el){
21035 //            var t = date.getTime();
21036 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21037 //                Roo.log('using add remove');
21038 //                
21039 //                this.fireEvent('monthchange', this, date);
21040 //                
21041 //                this.cells.removeClass("fc-state-highlight");
21042 //                this.cells.each(function(c){
21043 //                   if(c.dateValue == t){
21044 //                       c.addClass("fc-state-highlight");
21045 //                       setTimeout(function(){
21046 //                            try{c.dom.firstChild.focus();}catch(e){}
21047 //                       }, 50);
21048 //                       return false;
21049 //                   }
21050 //                   return true;
21051 //                });
21052 //                return;
21053 //            }
21054 //        }
21055         
21056         var days = date.getDaysInMonth();
21057         
21058         var firstOfMonth = date.getFirstDateOfMonth();
21059         var startingPos = firstOfMonth.getDay()-this.startDay;
21060         
21061         if(startingPos < this.startDay){
21062             startingPos += 7;
21063         }
21064         
21065         var pm = date.add(Date.MONTH, -1);
21066         var prevStart = pm.getDaysInMonth()-startingPos;
21067 //        
21068         this.cells = this.el.select('.fc-day',true);
21069         this.textNodes = this.el.query('.fc-day-number');
21070         this.cells.addClassOnOver('fc-state-hover');
21071         
21072         var cells = this.cells.elements;
21073         var textEls = this.textNodes;
21074         
21075         Roo.each(cells, function(cell){
21076             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21077         });
21078         
21079         days += startingPos;
21080
21081         // convert everything to numbers so it's fast
21082         var day = 86400000;
21083         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21084         //Roo.log(d);
21085         //Roo.log(pm);
21086         //Roo.log(prevStart);
21087         
21088         var today = new Date().clearTime().getTime();
21089         var sel = date.clearTime().getTime();
21090         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21091         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21092         var ddMatch = this.disabledDatesRE;
21093         var ddText = this.disabledDatesText;
21094         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21095         var ddaysText = this.disabledDaysText;
21096         var format = this.format;
21097         
21098         var setCellClass = function(cal, cell){
21099             cell.row = 0;
21100             cell.events = [];
21101             cell.more = [];
21102             //Roo.log('set Cell Class');
21103             cell.title = "";
21104             var t = d.getTime();
21105             
21106             //Roo.log(d);
21107             
21108             cell.dateValue = t;
21109             if(t == today){
21110                 cell.className += " fc-today";
21111                 cell.className += " fc-state-highlight";
21112                 cell.title = cal.todayText;
21113             }
21114             if(t == sel){
21115                 // disable highlight in other month..
21116                 //cell.className += " fc-state-highlight";
21117                 
21118             }
21119             // disabling
21120             if(t < min) {
21121                 cell.className = " fc-state-disabled";
21122                 cell.title = cal.minText;
21123                 return;
21124             }
21125             if(t > max) {
21126                 cell.className = " fc-state-disabled";
21127                 cell.title = cal.maxText;
21128                 return;
21129             }
21130             if(ddays){
21131                 if(ddays.indexOf(d.getDay()) != -1){
21132                     cell.title = ddaysText;
21133                     cell.className = " fc-state-disabled";
21134                 }
21135             }
21136             if(ddMatch && format){
21137                 var fvalue = d.dateFormat(format);
21138                 if(ddMatch.test(fvalue)){
21139                     cell.title = ddText.replace("%0", fvalue);
21140                     cell.className = " fc-state-disabled";
21141                 }
21142             }
21143             
21144             if (!cell.initialClassName) {
21145                 cell.initialClassName = cell.dom.className;
21146             }
21147             
21148             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21149         };
21150
21151         var i = 0;
21152         
21153         for(; i < startingPos; i++) {
21154             textEls[i].innerHTML = (++prevStart);
21155             d.setDate(d.getDate()+1);
21156             
21157             cells[i].className = "fc-past fc-other-month";
21158             setCellClass(this, cells[i]);
21159         }
21160         
21161         var intDay = 0;
21162         
21163         for(; i < days; i++){
21164             intDay = i - startingPos + 1;
21165             textEls[i].innerHTML = (intDay);
21166             d.setDate(d.getDate()+1);
21167             
21168             cells[i].className = ''; // "x-date-active";
21169             setCellClass(this, cells[i]);
21170         }
21171         var extraDays = 0;
21172         
21173         for(; i < 42; i++) {
21174             textEls[i].innerHTML = (++extraDays);
21175             d.setDate(d.getDate()+1);
21176             
21177             cells[i].className = "fc-future fc-other-month";
21178             setCellClass(this, cells[i]);
21179         }
21180         
21181         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21182         
21183         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21184         
21185         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21186         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21187         
21188         if(totalRows != 6){
21189             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21190             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21191         }
21192         
21193         this.fireEvent('monthchange', this, date);
21194         
21195         
21196         /*
21197         if(!this.internalRender){
21198             var main = this.el.dom.firstChild;
21199             var w = main.offsetWidth;
21200             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21201             Roo.fly(main).setWidth(w);
21202             this.internalRender = true;
21203             // opera does not respect the auto grow header center column
21204             // then, after it gets a width opera refuses to recalculate
21205             // without a second pass
21206             if(Roo.isOpera && !this.secondPass){
21207                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21208                 this.secondPass = true;
21209                 this.update.defer(10, this, [date]);
21210             }
21211         }
21212         */
21213         
21214     },
21215     
21216     findCell : function(dt) {
21217         dt = dt.clearTime().getTime();
21218         var ret = false;
21219         this.cells.each(function(c){
21220             //Roo.log("check " +c.dateValue + '?=' + dt);
21221             if(c.dateValue == dt){
21222                 ret = c;
21223                 return false;
21224             }
21225             return true;
21226         });
21227         
21228         return ret;
21229     },
21230     
21231     findCells : function(ev) {
21232         var s = ev.start.clone().clearTime().getTime();
21233        // Roo.log(s);
21234         var e= ev.end.clone().clearTime().getTime();
21235        // Roo.log(e);
21236         var ret = [];
21237         this.cells.each(function(c){
21238              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21239             
21240             if(c.dateValue > e){
21241                 return ;
21242             }
21243             if(c.dateValue < s){
21244                 return ;
21245             }
21246             ret.push(c);
21247         });
21248         
21249         return ret;    
21250     },
21251     
21252 //    findBestRow: function(cells)
21253 //    {
21254 //        var ret = 0;
21255 //        
21256 //        for (var i =0 ; i < cells.length;i++) {
21257 //            ret  = Math.max(cells[i].rows || 0,ret);
21258 //        }
21259 //        return ret;
21260 //        
21261 //    },
21262     
21263     
21264     addItem : function(ev)
21265     {
21266         // look for vertical location slot in
21267         var cells = this.findCells(ev);
21268         
21269 //        ev.row = this.findBestRow(cells);
21270         
21271         // work out the location.
21272         
21273         var crow = false;
21274         var rows = [];
21275         for(var i =0; i < cells.length; i++) {
21276             
21277             cells[i].row = cells[0].row;
21278             
21279             if(i == 0){
21280                 cells[i].row = cells[i].row + 1;
21281             }
21282             
21283             if (!crow) {
21284                 crow = {
21285                     start : cells[i],
21286                     end :  cells[i]
21287                 };
21288                 continue;
21289             }
21290             if (crow.start.getY() == cells[i].getY()) {
21291                 // on same row.
21292                 crow.end = cells[i];
21293                 continue;
21294             }
21295             // different row.
21296             rows.push(crow);
21297             crow = {
21298                 start: cells[i],
21299                 end : cells[i]
21300             };
21301             
21302         }
21303         
21304         rows.push(crow);
21305         ev.els = [];
21306         ev.rows = rows;
21307         ev.cells = cells;
21308         
21309         cells[0].events.push(ev);
21310         
21311         this.calevents.push(ev);
21312     },
21313     
21314     clearEvents: function() {
21315         
21316         if(!this.calevents){
21317             return;
21318         }
21319         
21320         Roo.each(this.cells.elements, function(c){
21321             c.row = 0;
21322             c.events = [];
21323             c.more = [];
21324         });
21325         
21326         Roo.each(this.calevents, function(e) {
21327             Roo.each(e.els, function(el) {
21328                 el.un('mouseenter' ,this.onEventEnter, this);
21329                 el.un('mouseleave' ,this.onEventLeave, this);
21330                 el.remove();
21331             },this);
21332         },this);
21333         
21334         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21335             e.remove();
21336         });
21337         
21338     },
21339     
21340     renderEvents: function()
21341     {   
21342         var _this = this;
21343         
21344         this.cells.each(function(c) {
21345             
21346             if(c.row < 5){
21347                 return;
21348             }
21349             
21350             var ev = c.events;
21351             
21352             var r = 4;
21353             if(c.row != c.events.length){
21354                 r = 4 - (4 - (c.row - c.events.length));
21355             }
21356             
21357             c.events = ev.slice(0, r);
21358             c.more = ev.slice(r);
21359             
21360             if(c.more.length && c.more.length == 1){
21361                 c.events.push(c.more.pop());
21362             }
21363             
21364             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21365             
21366         });
21367             
21368         this.cells.each(function(c) {
21369             
21370             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21371             
21372             
21373             for (var e = 0; e < c.events.length; e++){
21374                 var ev = c.events[e];
21375                 var rows = ev.rows;
21376                 
21377                 for(var i = 0; i < rows.length; i++) {
21378                 
21379                     // how many rows should it span..
21380
21381                     var  cfg = {
21382                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21383                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21384
21385                         unselectable : "on",
21386                         cn : [
21387                             {
21388                                 cls: 'fc-event-inner',
21389                                 cn : [
21390     //                                {
21391     //                                  tag:'span',
21392     //                                  cls: 'fc-event-time',
21393     //                                  html : cells.length > 1 ? '' : ev.time
21394     //                                },
21395                                     {
21396                                       tag:'span',
21397                                       cls: 'fc-event-title',
21398                                       html : String.format('{0}', ev.title)
21399                                     }
21400
21401
21402                                 ]
21403                             },
21404                             {
21405                                 cls: 'ui-resizable-handle ui-resizable-e',
21406                                 html : '&nbsp;&nbsp;&nbsp'
21407                             }
21408
21409                         ]
21410                     };
21411
21412                     if (i == 0) {
21413                         cfg.cls += ' fc-event-start';
21414                     }
21415                     if ((i+1) == rows.length) {
21416                         cfg.cls += ' fc-event-end';
21417                     }
21418
21419                     var ctr = _this.el.select('.fc-event-container',true).first();
21420                     var cg = ctr.createChild(cfg);
21421
21422                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21423                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21424
21425                     var r = (c.more.length) ? 1 : 0;
21426                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21427                     cg.setWidth(ebox.right - sbox.x -2);
21428
21429                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21430                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21431                     cg.on('click', _this.onEventClick, _this, ev);
21432
21433                     ev.els.push(cg);
21434                     
21435                 }
21436                 
21437             }
21438             
21439             
21440             if(c.more.length){
21441                 var  cfg = {
21442                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21443                     style : 'position: absolute',
21444                     unselectable : "on",
21445                     cn : [
21446                         {
21447                             cls: 'fc-event-inner',
21448                             cn : [
21449                                 {
21450                                   tag:'span',
21451                                   cls: 'fc-event-title',
21452                                   html : 'More'
21453                                 }
21454
21455
21456                             ]
21457                         },
21458                         {
21459                             cls: 'ui-resizable-handle ui-resizable-e',
21460                             html : '&nbsp;&nbsp;&nbsp'
21461                         }
21462
21463                     ]
21464                 };
21465
21466                 var ctr = _this.el.select('.fc-event-container',true).first();
21467                 var cg = ctr.createChild(cfg);
21468
21469                 var sbox = c.select('.fc-day-content',true).first().getBox();
21470                 var ebox = c.select('.fc-day-content',true).first().getBox();
21471                 //Roo.log(cg);
21472                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21473                 cg.setWidth(ebox.right - sbox.x -2);
21474
21475                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21476                 
21477             }
21478             
21479         });
21480         
21481         
21482         
21483     },
21484     
21485     onEventEnter: function (e, el,event,d) {
21486         this.fireEvent('evententer', this, el, event);
21487     },
21488     
21489     onEventLeave: function (e, el,event,d) {
21490         this.fireEvent('eventleave', this, el, event);
21491     },
21492     
21493     onEventClick: function (e, el,event,d) {
21494         this.fireEvent('eventclick', this, el, event);
21495     },
21496     
21497     onMonthChange: function () {
21498         this.store.load();
21499     },
21500     
21501     onMoreEventClick: function(e, el, more)
21502     {
21503         var _this = this;
21504         
21505         this.calpopover.placement = 'right';
21506         this.calpopover.setTitle('More');
21507         
21508         this.calpopover.setContent('');
21509         
21510         var ctr = this.calpopover.el.select('.popover-content', true).first();
21511         
21512         Roo.each(more, function(m){
21513             var cfg = {
21514                 cls : 'fc-event-hori fc-event-draggable',
21515                 html : m.title
21516             };
21517             var cg = ctr.createChild(cfg);
21518             
21519             cg.on('click', _this.onEventClick, _this, m);
21520         });
21521         
21522         this.calpopover.show(el);
21523         
21524         
21525     },
21526     
21527     onLoad: function () 
21528     {   
21529         this.calevents = [];
21530         var cal = this;
21531         
21532         if(this.store.getCount() > 0){
21533             this.store.data.each(function(d){
21534                cal.addItem({
21535                     id : d.data.id,
21536                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21537                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21538                     time : d.data.start_time,
21539                     title : d.data.title,
21540                     description : d.data.description,
21541                     venue : d.data.venue
21542                 });
21543             });
21544         }
21545         
21546         this.renderEvents();
21547         
21548         if(this.calevents.length && this.loadMask){
21549             this.maskEl.hide();
21550         }
21551     },
21552     
21553     onBeforeLoad: function()
21554     {
21555         this.clearEvents();
21556         if(this.loadMask){
21557             this.maskEl.show();
21558         }
21559     }
21560 });
21561
21562  
21563  /*
21564  * - LGPL
21565  *
21566  * element
21567  * 
21568  */
21569
21570 /**
21571  * @class Roo.bootstrap.Popover
21572  * @extends Roo.bootstrap.Component
21573  * @parent none builder
21574  * @children Roo.bootstrap.Component
21575  * Bootstrap Popover class
21576  * @cfg {String} html contents of the popover   (or false to use children..)
21577  * @cfg {String} title of popover (or false to hide)
21578  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21579  * @cfg {String} trigger click || hover (or false to trigger manually)
21580  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21581  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21582  *      - if false and it has a 'parent' then it will be automatically added to that element
21583  *      - if string - Roo.get  will be called 
21584  * @cfg {Number} delay - delay before showing
21585  
21586  * @constructor
21587  * Create a new Popover
21588  * @param {Object} config The config object
21589  */
21590
21591 Roo.bootstrap.Popover = function(config){
21592     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21593     
21594     this.addEvents({
21595         // raw events
21596          /**
21597          * @event show
21598          * After the popover show
21599          * 
21600          * @param {Roo.bootstrap.Popover} this
21601          */
21602         "show" : true,
21603         /**
21604          * @event hide
21605          * After the popover hide
21606          * 
21607          * @param {Roo.bootstrap.Popover} this
21608          */
21609         "hide" : true
21610     });
21611 };
21612
21613 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21614     
21615     title: false,
21616     html: false,
21617     
21618     placement : 'right',
21619     trigger : 'hover', // hover
21620     modal : false,
21621     delay : 0,
21622     
21623     over: false,
21624     
21625     can_build_overlaid : false,
21626     
21627     maskEl : false, // the mask element
21628     headerEl : false,
21629     contentEl : false,
21630     alignEl : false, // when show is called with an element - this get's stored.
21631     
21632     getChildContainer : function()
21633     {
21634         return this.contentEl;
21635         
21636     },
21637     getPopoverHeader : function()
21638     {
21639         this.title = true; // flag not to hide it..
21640         this.headerEl.addClass('p-0');
21641         return this.headerEl
21642     },
21643     
21644     
21645     getAutoCreate : function(){
21646          
21647         var cfg = {
21648            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21649            style: 'display:block',
21650            cn : [
21651                 {
21652                     cls : 'arrow'
21653                 },
21654                 {
21655                     cls : 'popover-inner ',
21656                     cn : [
21657                         {
21658                             tag: 'h3',
21659                             cls: 'popover-title popover-header',
21660                             html : this.title === false ? '' : this.title
21661                         },
21662                         {
21663                             cls : 'popover-content popover-body '  + (this.cls || ''),
21664                             html : this.html || ''
21665                         }
21666                     ]
21667                     
21668                 }
21669            ]
21670         };
21671         
21672         return cfg;
21673     },
21674     /**
21675      * @param {string} the title
21676      */
21677     setTitle: function(str)
21678     {
21679         this.title = str;
21680         if (this.el) {
21681             this.headerEl.dom.innerHTML = str;
21682         }
21683         
21684     },
21685     /**
21686      * @param {string} the body content
21687      */
21688     setContent: function(str)
21689     {
21690         this.html = str;
21691         if (this.contentEl) {
21692             this.contentEl.dom.innerHTML = str;
21693         }
21694         
21695     },
21696     // as it get's added to the bottom of the page.
21697     onRender : function(ct, position)
21698     {
21699         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21700         
21701         
21702         
21703         if(!this.el){
21704             var cfg = Roo.apply({},  this.getAutoCreate());
21705             cfg.id = Roo.id();
21706             
21707             if (this.cls) {
21708                 cfg.cls += ' ' + this.cls;
21709             }
21710             if (this.style) {
21711                 cfg.style = this.style;
21712             }
21713             //Roo.log("adding to ");
21714             this.el = Roo.get(document.body).createChild(cfg, position);
21715 //            Roo.log(this.el);
21716         }
21717         
21718         this.contentEl = this.el.select('.popover-content',true).first();
21719         this.headerEl =  this.el.select('.popover-title',true).first();
21720         
21721         var nitems = [];
21722         if(typeof(this.items) != 'undefined'){
21723             var items = this.items;
21724             delete this.items;
21725
21726             for(var i =0;i < items.length;i++) {
21727                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21728             }
21729         }
21730
21731         this.items = nitems;
21732         
21733         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21734         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21735         
21736         
21737         
21738         this.initEvents();
21739     },
21740     
21741     resizeMask : function()
21742     {
21743         this.maskEl.setSize(
21744             Roo.lib.Dom.getViewWidth(true),
21745             Roo.lib.Dom.getViewHeight(true)
21746         );
21747     },
21748     
21749     initEvents : function()
21750     {
21751         
21752         if (!this.modal) { 
21753             Roo.bootstrap.Popover.register(this);
21754         }
21755          
21756         this.arrowEl = this.el.select('.arrow',true).first();
21757         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21758         this.el.enableDisplayMode('block');
21759         this.el.hide();
21760  
21761         
21762         if (this.over === false && !this.parent()) {
21763             return; 
21764         }
21765         if (this.triggers === false) {
21766             return;
21767         }
21768          
21769         // support parent
21770         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21771         var triggers = this.trigger ? this.trigger.split(' ') : [];
21772         Roo.each(triggers, function(trigger) {
21773         
21774             if (trigger == 'click') {
21775                 on_el.on('click', this.toggle, this);
21776             } else if (trigger != 'manual') {
21777                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21778                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21779       
21780                 on_el.on(eventIn  ,this.enter, this);
21781                 on_el.on(eventOut, this.leave, this);
21782             }
21783         }, this);
21784     },
21785     
21786     
21787     // private
21788     timeout : null,
21789     hoverState : null,
21790     
21791     toggle : function () {
21792         this.hoverState == 'in' ? this.leave() : this.enter();
21793     },
21794     
21795     enter : function () {
21796         
21797         clearTimeout(this.timeout);
21798     
21799         this.hoverState = 'in';
21800     
21801         if (!this.delay || !this.delay.show) {
21802             this.show();
21803             return;
21804         }
21805         var _t = this;
21806         this.timeout = setTimeout(function () {
21807             if (_t.hoverState == 'in') {
21808                 _t.show();
21809             }
21810         }, this.delay.show)
21811     },
21812     
21813     leave : function() {
21814         clearTimeout(this.timeout);
21815     
21816         this.hoverState = 'out';
21817     
21818         if (!this.delay || !this.delay.hide) {
21819             this.hide();
21820             return;
21821         }
21822         var _t = this;
21823         this.timeout = setTimeout(function () {
21824             if (_t.hoverState == 'out') {
21825                 _t.hide();
21826             }
21827         }, this.delay.hide)
21828     },
21829     
21830     /**
21831      * update the position of the dialog
21832      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21833      * 
21834      *
21835      */
21836     
21837     doAlign : function()
21838     {
21839         
21840         if (this.alignEl) {
21841             this.updatePosition(this.placement, true);
21842              
21843         } else {
21844             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21845             var es = this.el.getSize();
21846             var x = Roo.lib.Dom.getViewWidth()/2;
21847             var y = Roo.lib.Dom.getViewHeight()/2;
21848             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21849             
21850         }
21851
21852          
21853          
21854         
21855         
21856     },
21857     
21858     /**
21859      * Show the popover
21860      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21861      * @param {string} (left|right|top|bottom) position
21862      */
21863     show : function (on_el, placement)
21864     {
21865         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21866         on_el = on_el || false; // default to false
21867          
21868         if (!on_el) {
21869             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21870                 on_el = this.parent().el;
21871             } else if (this.over) {
21872                 on_el = Roo.get(this.over);
21873             }
21874             
21875         }
21876         
21877         this.alignEl = Roo.get( on_el );
21878
21879         if (!this.el) {
21880             this.render(document.body);
21881         }
21882         
21883         
21884          
21885         
21886         if (this.title === false) {
21887             this.headerEl.hide();
21888         }
21889         
21890        
21891         this.el.show();
21892         this.el.dom.style.display = 'block';
21893          
21894         this.doAlign();
21895         
21896         //var arrow = this.el.select('.arrow',true).first();
21897         //arrow.set(align[2], 
21898         
21899         this.el.addClass('in');
21900         
21901          
21902         
21903         this.hoverState = 'in';
21904         
21905         if (this.modal) {
21906             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21907             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21908             this.maskEl.dom.style.display = 'block';
21909             this.maskEl.addClass('show');
21910         }
21911         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21912  
21913         this.fireEvent('show', this);
21914         
21915     },
21916     /**
21917      * fire this manually after loading a grid in the table for example
21918      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21919      * @param {Boolean} try and move it if we cant get right position.
21920      */
21921     updatePosition : function(placement, try_move)
21922     {
21923         // allow for calling with no parameters
21924         placement = placement   ? placement :  this.placement;
21925         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21926         
21927         this.el.removeClass([
21928             'fade','top','bottom', 'left', 'right','in',
21929             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21930         ]);
21931         this.el.addClass(placement + ' bs-popover-' + placement);
21932         
21933         if (!this.alignEl ) {
21934             return false;
21935         }
21936         
21937         switch (placement) {
21938             case 'right':
21939                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21940                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21941                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21942                     //normal display... or moved up/down.
21943                     this.el.setXY(offset);
21944                     var xy = this.alignEl.getAnchorXY('tr', false);
21945                     xy[0]+=2;xy[1]+=5;
21946                     this.arrowEl.setXY(xy);
21947                     return true;
21948                 }
21949                 // continue through...
21950                 return this.updatePosition('left', false);
21951                 
21952             
21953             case 'left':
21954                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21955                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21956                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21957                     //normal display... or moved up/down.
21958                     this.el.setXY(offset);
21959                     var xy = this.alignEl.getAnchorXY('tl', false);
21960                     xy[0]-=10;xy[1]+=5; // << fix me
21961                     this.arrowEl.setXY(xy);
21962                     return true;
21963                 }
21964                 // call self...
21965                 return this.updatePosition('right', false);
21966             
21967             case 'top':
21968                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21969                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21970                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21971                     //normal display... or moved up/down.
21972                     this.el.setXY(offset);
21973                     var xy = this.alignEl.getAnchorXY('t', false);
21974                     xy[1]-=10; // << fix me
21975                     this.arrowEl.setXY(xy);
21976                     return true;
21977                 }
21978                 // fall through
21979                return this.updatePosition('bottom', false);
21980             
21981             case 'bottom':
21982                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21983                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21984                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21985                     //normal display... or moved up/down.
21986                     this.el.setXY(offset);
21987                     var xy = this.alignEl.getAnchorXY('b', false);
21988                      xy[1]+=2; // << fix me
21989                     this.arrowEl.setXY(xy);
21990                     return true;
21991                 }
21992                 // fall through
21993                 return this.updatePosition('top', false);
21994                 
21995             
21996         }
21997         
21998         
21999         return false;
22000     },
22001     
22002     hide : function()
22003     {
22004         this.el.setXY([0,0]);
22005         this.el.removeClass('in');
22006         this.el.hide();
22007         this.hoverState = null;
22008         this.maskEl.hide(); // always..
22009         this.fireEvent('hide', this);
22010     }
22011     
22012 });
22013
22014
22015 Roo.apply(Roo.bootstrap.Popover, {
22016
22017     alignment : {
22018         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22019         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22020         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22021         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22022     },
22023     
22024     zIndex : 20001,
22025
22026     clickHander : false,
22027     
22028     
22029
22030     onMouseDown : function(e)
22031     {
22032         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22033             /// what is nothing is showing..
22034             this.hideAll();
22035         }
22036          
22037     },
22038     
22039     
22040     popups : [],
22041     
22042     register : function(popup)
22043     {
22044         if (!Roo.bootstrap.Popover.clickHandler) {
22045             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22046         }
22047         // hide other popups.
22048         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22049         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22050         this.hideAll(); //<< why?
22051         //this.popups.push(popup);
22052     },
22053     hideAll : function()
22054     {
22055         this.popups.forEach(function(p) {
22056             p.hide();
22057         });
22058     },
22059     onShow : function() {
22060         Roo.bootstrap.Popover.popups.push(this);
22061     },
22062     onHide : function() {
22063         Roo.bootstrap.Popover.popups.remove(this);
22064     } 
22065
22066 });
22067 /**
22068  * @class Roo.bootstrap.PopoverNav
22069  * @extends Roo.bootstrap.nav.Simplebar
22070  * @parent Roo.bootstrap.Popover
22071  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22072  * @licence LGPL
22073  * Bootstrap Popover header navigation class
22074  * FIXME? should this go under nav?
22075  *
22076  * 
22077  * @constructor
22078  * Create a new Popover Header Navigation 
22079  * @param {Object} config The config object
22080  */
22081
22082 Roo.bootstrap.PopoverNav = function(config){
22083     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22084 };
22085
22086 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22087     
22088     
22089     container_method : 'getPopoverHeader' 
22090     
22091      
22092     
22093     
22094    
22095 });
22096
22097  
22098
22099  /*
22100  * - LGPL
22101  *
22102  * Progress
22103  * 
22104  */
22105
22106 /**
22107  * @class Roo.bootstrap.Progress
22108  * @extends Roo.bootstrap.Component
22109  * @children Roo.bootstrap.ProgressBar
22110  * Bootstrap Progress class
22111  * @cfg {Boolean} striped striped of the progress bar
22112  * @cfg {Boolean} active animated of the progress bar
22113  * 
22114  * 
22115  * @constructor
22116  * Create a new Progress
22117  * @param {Object} config The config object
22118  */
22119
22120 Roo.bootstrap.Progress = function(config){
22121     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22122 };
22123
22124 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22125     
22126     striped : false,
22127     active: false,
22128     
22129     getAutoCreate : function(){
22130         var cfg = {
22131             tag: 'div',
22132             cls: 'progress'
22133         };
22134         
22135         
22136         if(this.striped){
22137             cfg.cls += ' progress-striped';
22138         }
22139       
22140         if(this.active){
22141             cfg.cls += ' active';
22142         }
22143         
22144         
22145         return cfg;
22146     }
22147    
22148 });
22149
22150  
22151
22152  /*
22153  * - LGPL
22154  *
22155  * ProgressBar
22156  * 
22157  */
22158
22159 /**
22160  * @class Roo.bootstrap.ProgressBar
22161  * @extends Roo.bootstrap.Component
22162  * Bootstrap ProgressBar class
22163  * @cfg {Number} aria_valuenow aria-value now
22164  * @cfg {Number} aria_valuemin aria-value min
22165  * @cfg {Number} aria_valuemax aria-value max
22166  * @cfg {String} label label for the progress bar
22167  * @cfg {String} panel (success | info | warning | danger )
22168  * @cfg {String} role role of the progress bar
22169  * @cfg {String} sr_only text
22170  * 
22171  * 
22172  * @constructor
22173  * Create a new ProgressBar
22174  * @param {Object} config The config object
22175  */
22176
22177 Roo.bootstrap.ProgressBar = function(config){
22178     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22179 };
22180
22181 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22182     
22183     aria_valuenow : 0,
22184     aria_valuemin : 0,
22185     aria_valuemax : 100,
22186     label : false,
22187     panel : false,
22188     role : false,
22189     sr_only: false,
22190     
22191     getAutoCreate : function()
22192     {
22193         
22194         var cfg = {
22195             tag: 'div',
22196             cls: 'progress-bar',
22197             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22198         };
22199         
22200         if(this.sr_only){
22201             cfg.cn = {
22202                 tag: 'span',
22203                 cls: 'sr-only',
22204                 html: this.sr_only
22205             }
22206         }
22207         
22208         if(this.role){
22209             cfg.role = this.role;
22210         }
22211         
22212         if(this.aria_valuenow){
22213             cfg['aria-valuenow'] = this.aria_valuenow;
22214         }
22215         
22216         if(this.aria_valuemin){
22217             cfg['aria-valuemin'] = this.aria_valuemin;
22218         }
22219         
22220         if(this.aria_valuemax){
22221             cfg['aria-valuemax'] = this.aria_valuemax;
22222         }
22223         
22224         if(this.label && !this.sr_only){
22225             cfg.html = this.label;
22226         }
22227         
22228         if(this.panel){
22229             cfg.cls += ' progress-bar-' + this.panel;
22230         }
22231         
22232         return cfg;
22233     },
22234     
22235     update : function(aria_valuenow)
22236     {
22237         this.aria_valuenow = aria_valuenow;
22238         
22239         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22240     }
22241    
22242 });
22243
22244  
22245
22246  /**
22247  * @class Roo.bootstrap.TabGroup
22248  * @extends Roo.bootstrap.Column
22249  * @children Roo.bootstrap.TabPanel
22250  * Bootstrap Column class
22251  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22252  * @cfg {Boolean} carousel true to make the group behave like a carousel
22253  * @cfg {Boolean} bullets show bullets for the panels
22254  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22255  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22256  * @cfg {Boolean} showarrow (true|false) show arrow default true
22257  * 
22258  * @constructor
22259  * Create a new TabGroup
22260  * @param {Object} config The config object
22261  */
22262
22263 Roo.bootstrap.TabGroup = function(config){
22264     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22265     if (!this.navId) {
22266         this.navId = Roo.id();
22267     }
22268     this.tabs = [];
22269     Roo.bootstrap.TabGroup.register(this);
22270     
22271 };
22272
22273 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22274     
22275     carousel : false,
22276     transition : false,
22277     bullets : 0,
22278     timer : 0,
22279     autoslide : false,
22280     slideFn : false,
22281     slideOnTouch : false,
22282     showarrow : true,
22283     
22284     getAutoCreate : function()
22285     {
22286         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22287         
22288         cfg.cls += ' tab-content';
22289         
22290         if (this.carousel) {
22291             cfg.cls += ' carousel slide';
22292             
22293             cfg.cn = [{
22294                cls : 'carousel-inner',
22295                cn : []
22296             }];
22297         
22298             if(this.bullets  && !Roo.isTouch){
22299                 
22300                 var bullets = {
22301                     cls : 'carousel-bullets',
22302                     cn : []
22303                 };
22304                
22305                 if(this.bullets_cls){
22306                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22307                 }
22308                 
22309                 bullets.cn.push({
22310                     cls : 'clear'
22311                 });
22312                 
22313                 cfg.cn[0].cn.push(bullets);
22314             }
22315             
22316             if(this.showarrow){
22317                 cfg.cn[0].cn.push({
22318                     tag : 'div',
22319                     class : 'carousel-arrow',
22320                     cn : [
22321                         {
22322                             tag : 'div',
22323                             class : 'carousel-prev',
22324                             cn : [
22325                                 {
22326                                     tag : 'i',
22327                                     class : 'fa fa-chevron-left'
22328                                 }
22329                             ]
22330                         },
22331                         {
22332                             tag : 'div',
22333                             class : 'carousel-next',
22334                             cn : [
22335                                 {
22336                                     tag : 'i',
22337                                     class : 'fa fa-chevron-right'
22338                                 }
22339                             ]
22340                         }
22341                     ]
22342                 });
22343             }
22344             
22345         }
22346         
22347         return cfg;
22348     },
22349     
22350     initEvents:  function()
22351     {
22352 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22353 //            this.el.on("touchstart", this.onTouchStart, this);
22354 //        }
22355         
22356         if(this.autoslide){
22357             var _this = this;
22358             
22359             this.slideFn = window.setInterval(function() {
22360                 _this.showPanelNext();
22361             }, this.timer);
22362         }
22363         
22364         if(this.showarrow){
22365             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22366             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22367         }
22368         
22369         
22370     },
22371     
22372 //    onTouchStart : function(e, el, o)
22373 //    {
22374 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22375 //            return;
22376 //        }
22377 //        
22378 //        this.showPanelNext();
22379 //    },
22380     
22381     
22382     getChildContainer : function()
22383     {
22384         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22385     },
22386     
22387     /**
22388     * register a Navigation item
22389     * @param {Roo.bootstrap.nav.Item} the navitem to add
22390     */
22391     register : function(item)
22392     {
22393         this.tabs.push( item);
22394         item.navId = this.navId; // not really needed..
22395         this.addBullet();
22396     
22397     },
22398     
22399     getActivePanel : function()
22400     {
22401         var r = false;
22402         Roo.each(this.tabs, function(t) {
22403             if (t.active) {
22404                 r = t;
22405                 return false;
22406             }
22407             return null;
22408         });
22409         return r;
22410         
22411     },
22412     getPanelByName : function(n)
22413     {
22414         var r = false;
22415         Roo.each(this.tabs, function(t) {
22416             if (t.tabId == n) {
22417                 r = t;
22418                 return false;
22419             }
22420             return null;
22421         });
22422         return r;
22423     },
22424     indexOfPanel : function(p)
22425     {
22426         var r = false;
22427         Roo.each(this.tabs, function(t,i) {
22428             if (t.tabId == p.tabId) {
22429                 r = i;
22430                 return false;
22431             }
22432             return null;
22433         });
22434         return r;
22435     },
22436     /**
22437      * show a specific panel
22438      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22439      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22440      */
22441     showPanel : function (pan)
22442     {
22443         if(this.transition || typeof(pan) == 'undefined'){
22444             Roo.log("waiting for the transitionend");
22445             return false;
22446         }
22447         
22448         if (typeof(pan) == 'number') {
22449             pan = this.tabs[pan];
22450         }
22451         
22452         if (typeof(pan) == 'string') {
22453             pan = this.getPanelByName(pan);
22454         }
22455         
22456         var cur = this.getActivePanel();
22457         
22458         if(!pan || !cur){
22459             Roo.log('pan or acitve pan is undefined');
22460             return false;
22461         }
22462         
22463         if (pan.tabId == this.getActivePanel().tabId) {
22464             return true;
22465         }
22466         
22467         if (false === cur.fireEvent('beforedeactivate')) {
22468             return false;
22469         }
22470         
22471         if(this.bullets > 0 && !Roo.isTouch){
22472             this.setActiveBullet(this.indexOfPanel(pan));
22473         }
22474         
22475         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22476             
22477             //class="carousel-item carousel-item-next carousel-item-left"
22478             
22479             this.transition = true;
22480             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22481             var lr = dir == 'next' ? 'left' : 'right';
22482             pan.el.addClass(dir); // or prev
22483             pan.el.addClass('carousel-item-' + dir); // or prev
22484             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22485             cur.el.addClass(lr); // or right
22486             pan.el.addClass(lr);
22487             cur.el.addClass('carousel-item-' +lr); // or right
22488             pan.el.addClass('carousel-item-' +lr);
22489             
22490             
22491             var _this = this;
22492             cur.el.on('transitionend', function() {
22493                 Roo.log("trans end?");
22494                 
22495                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22496                 pan.setActive(true);
22497                 
22498                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22499                 cur.setActive(false);
22500                 
22501                 _this.transition = false;
22502                 
22503             }, this, { single:  true } );
22504             
22505             return true;
22506         }
22507         
22508         cur.setActive(false);
22509         pan.setActive(true);
22510         
22511         return true;
22512         
22513     },
22514     showPanelNext : function()
22515     {
22516         var i = this.indexOfPanel(this.getActivePanel());
22517         
22518         if (i >= this.tabs.length - 1 && !this.autoslide) {
22519             return;
22520         }
22521         
22522         if (i >= this.tabs.length - 1 && this.autoslide) {
22523             i = -1;
22524         }
22525         
22526         this.showPanel(this.tabs[i+1]);
22527     },
22528     
22529     showPanelPrev : function()
22530     {
22531         var i = this.indexOfPanel(this.getActivePanel());
22532         
22533         if (i  < 1 && !this.autoslide) {
22534             return;
22535         }
22536         
22537         if (i < 1 && this.autoslide) {
22538             i = this.tabs.length;
22539         }
22540         
22541         this.showPanel(this.tabs[i-1]);
22542     },
22543     
22544     
22545     addBullet: function()
22546     {
22547         if(!this.bullets || Roo.isTouch){
22548             return;
22549         }
22550         var ctr = this.el.select('.carousel-bullets',true).first();
22551         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22552         var bullet = ctr.createChild({
22553             cls : 'bullet bullet-' + i
22554         },ctr.dom.lastChild);
22555         
22556         
22557         var _this = this;
22558         
22559         bullet.on('click', (function(e, el, o, ii, t){
22560
22561             e.preventDefault();
22562
22563             this.showPanel(ii);
22564
22565             if(this.autoslide && this.slideFn){
22566                 clearInterval(this.slideFn);
22567                 this.slideFn = window.setInterval(function() {
22568                     _this.showPanelNext();
22569                 }, this.timer);
22570             }
22571
22572         }).createDelegate(this, [i, bullet], true));
22573                 
22574         
22575     },
22576      
22577     setActiveBullet : function(i)
22578     {
22579         if(Roo.isTouch){
22580             return;
22581         }
22582         
22583         Roo.each(this.el.select('.bullet', true).elements, function(el){
22584             el.removeClass('selected');
22585         });
22586
22587         var bullet = this.el.select('.bullet-' + i, true).first();
22588         
22589         if(!bullet){
22590             return;
22591         }
22592         
22593         bullet.addClass('selected');
22594     }
22595     
22596     
22597   
22598 });
22599
22600  
22601
22602  
22603  
22604 Roo.apply(Roo.bootstrap.TabGroup, {
22605     
22606     groups: {},
22607      /**
22608     * register a Navigation Group
22609     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22610     */
22611     register : function(navgrp)
22612     {
22613         this.groups[navgrp.navId] = navgrp;
22614         
22615     },
22616     /**
22617     * fetch a Navigation Group based on the navigation ID
22618     * if one does not exist , it will get created.
22619     * @param {string} the navgroup to add
22620     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22621     */
22622     get: function(navId) {
22623         if (typeof(this.groups[navId]) == 'undefined') {
22624             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22625         }
22626         return this.groups[navId] ;
22627     }
22628     
22629     
22630     
22631 });
22632
22633  /*
22634  * - LGPL
22635  *
22636  * TabPanel
22637  * 
22638  */
22639
22640 /**
22641  * @class Roo.bootstrap.TabPanel
22642  * @extends Roo.bootstrap.Component
22643  * @children Roo.bootstrap.Component
22644  * Bootstrap TabPanel class
22645  * @cfg {Boolean} active panel active
22646  * @cfg {String} html panel content
22647  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22648  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22649  * @cfg {String} href click to link..
22650  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22651  * 
22652  * 
22653  * @constructor
22654  * Create a new TabPanel
22655  * @param {Object} config The config object
22656  */
22657
22658 Roo.bootstrap.TabPanel = function(config){
22659     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22660     this.addEvents({
22661         /**
22662              * @event changed
22663              * Fires when the active status changes
22664              * @param {Roo.bootstrap.TabPanel} this
22665              * @param {Boolean} state the new state
22666             
22667          */
22668         'changed': true,
22669         /**
22670              * @event beforedeactivate
22671              * Fires before a tab is de-activated - can be used to do validation on a form.
22672              * @param {Roo.bootstrap.TabPanel} this
22673              * @return {Boolean} false if there is an error
22674             
22675          */
22676         'beforedeactivate': true
22677      });
22678     
22679     this.tabId = this.tabId || Roo.id();
22680   
22681 };
22682
22683 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22684     
22685     active: false,
22686     html: false,
22687     tabId: false,
22688     navId : false,
22689     href : '',
22690     touchSlide : false,
22691     getAutoCreate : function(){
22692         
22693         
22694         var cfg = {
22695             tag: 'div',
22696             // item is needed for carousel - not sure if it has any effect otherwise
22697             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22698             html: this.html || ''
22699         };
22700         
22701         if(this.active){
22702             cfg.cls += ' active';
22703         }
22704         
22705         if(this.tabId){
22706             cfg.tabId = this.tabId;
22707         }
22708         
22709         
22710         
22711         return cfg;
22712     },
22713     
22714     initEvents:  function()
22715     {
22716         var p = this.parent();
22717         
22718         this.navId = this.navId || p.navId;
22719         
22720         if (typeof(this.navId) != 'undefined') {
22721             // not really needed.. but just in case.. parent should be a NavGroup.
22722             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22723             
22724             tg.register(this);
22725             
22726             var i = tg.tabs.length - 1;
22727             
22728             if(this.active && tg.bullets > 0 && i < tg.bullets){
22729                 tg.setActiveBullet(i);
22730             }
22731         }
22732         
22733         this.el.on('click', this.onClick, this);
22734         
22735         if(Roo.isTouch && this.touchSlide){
22736             this.el.on("touchstart", this.onTouchStart, this);
22737             this.el.on("touchmove", this.onTouchMove, this);
22738             this.el.on("touchend", this.onTouchEnd, this);
22739         }
22740         
22741     },
22742     
22743     onRender : function(ct, position)
22744     {
22745         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22746     },
22747     
22748     setActive : function(state)
22749     {
22750         Roo.log("panel - set active " + this.tabId + "=" + state);
22751         
22752         this.active = state;
22753         if (!state) {
22754             this.el.removeClass('active');
22755             
22756         } else  if (!this.el.hasClass('active')) {
22757             this.el.addClass('active');
22758         }
22759         
22760         this.fireEvent('changed', this, state);
22761     },
22762     
22763     onClick : function(e)
22764     {
22765         e.preventDefault();
22766         
22767         if(!this.href.length){
22768             return;
22769         }
22770         
22771         window.location.href = this.href;
22772     },
22773     
22774     startX : 0,
22775     startY : 0,
22776     endX : 0,
22777     endY : 0,
22778     swiping : false,
22779     
22780     onTouchStart : function(e)
22781     {
22782         this.swiping = false;
22783         
22784         this.startX = e.browserEvent.touches[0].clientX;
22785         this.startY = e.browserEvent.touches[0].clientY;
22786     },
22787     
22788     onTouchMove : function(e)
22789     {
22790         this.swiping = true;
22791         
22792         this.endX = e.browserEvent.touches[0].clientX;
22793         this.endY = e.browserEvent.touches[0].clientY;
22794     },
22795     
22796     onTouchEnd : function(e)
22797     {
22798         if(!this.swiping){
22799             this.onClick(e);
22800             return;
22801         }
22802         
22803         var tabGroup = this.parent();
22804         
22805         if(this.endX > this.startX){ // swiping right
22806             tabGroup.showPanelPrev();
22807             return;
22808         }
22809         
22810         if(this.startX > this.endX){ // swiping left
22811             tabGroup.showPanelNext();
22812             return;
22813         }
22814     }
22815     
22816     
22817 });
22818  
22819
22820  
22821
22822  /*
22823  * - LGPL
22824  *
22825  * DateField
22826  * 
22827  */
22828
22829 /**
22830  * @class Roo.bootstrap.form.DateField
22831  * @extends Roo.bootstrap.form.Input
22832  * Bootstrap DateField class
22833  * @cfg {Number} weekStart default 0
22834  * @cfg {String} viewMode default empty, (months|years)
22835  * @cfg {String} minViewMode default empty, (months|years)
22836  * @cfg {Number} startDate default -Infinity
22837  * @cfg {Number} endDate default Infinity
22838  * @cfg {Boolean} todayHighlight default false
22839  * @cfg {Boolean} todayBtn default false
22840  * @cfg {Boolean} calendarWeeks default false
22841  * @cfg {Object} daysOfWeekDisabled default empty
22842  * @cfg {Boolean} singleMode default false (true | false)
22843  * 
22844  * @cfg {Boolean} keyboardNavigation default true
22845  * @cfg {String} language default en
22846  * 
22847  * @constructor
22848  * Create a new DateField
22849  * @param {Object} config The config object
22850  */
22851
22852 Roo.bootstrap.form.DateField = function(config){
22853     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22854      this.addEvents({
22855             /**
22856              * @event show
22857              * Fires when this field show.
22858              * @param {Roo.bootstrap.form.DateField} this
22859              * @param {Mixed} date The date value
22860              */
22861             show : true,
22862             /**
22863              * @event show
22864              * Fires when this field hide.
22865              * @param {Roo.bootstrap.form.DateField} this
22866              * @param {Mixed} date The date value
22867              */
22868             hide : true,
22869             /**
22870              * @event select
22871              * Fires when select a date.
22872              * @param {Roo.bootstrap.form.DateField} this
22873              * @param {Mixed} date The date value
22874              */
22875             select : true,
22876             /**
22877              * @event beforeselect
22878              * Fires when before select a date.
22879              * @param {Roo.bootstrap.form.DateField} this
22880              * @param {Mixed} date The date value
22881              */
22882             beforeselect : true
22883         });
22884 };
22885
22886 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22887     
22888     /**
22889      * @cfg {String} format
22890      * The default date format string which can be overriden for localization support.  The format must be
22891      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22892      */
22893     format : "m/d/y",
22894     /**
22895      * @cfg {String} altFormats
22896      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22897      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22898      */
22899     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22900     
22901     weekStart : 0,
22902     
22903     viewMode : '',
22904     
22905     minViewMode : '',
22906     
22907     todayHighlight : false,
22908     
22909     todayBtn: false,
22910     
22911     language: 'en',
22912     
22913     keyboardNavigation: true,
22914     
22915     calendarWeeks: false,
22916     
22917     startDate: -Infinity,
22918     
22919     endDate: Infinity,
22920     
22921     daysOfWeekDisabled: [],
22922     
22923     _events: [],
22924     
22925     singleMode : false,
22926     
22927     UTCDate: function()
22928     {
22929         return new Date(Date.UTC.apply(Date, arguments));
22930     },
22931     
22932     UTCToday: function()
22933     {
22934         var today = new Date();
22935         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22936     },
22937     
22938     getDate: function() {
22939             var d = this.getUTCDate();
22940             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22941     },
22942     
22943     getUTCDate: function() {
22944             return this.date;
22945     },
22946     
22947     setDate: function(d) {
22948             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22949     },
22950     
22951     setUTCDate: function(d) {
22952             this.date = d;
22953             this.setValue(this.formatDate(this.date));
22954     },
22955         
22956     onRender: function(ct, position)
22957     {
22958         
22959         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22960         
22961         this.language = this.language || 'en';
22962         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22963         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22964         
22965         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22966         this.format = this.format || 'm/d/y';
22967         this.isInline = false;
22968         this.isInput = true;
22969         this.component = this.el.select('.add-on', true).first() || false;
22970         this.component = (this.component && this.component.length === 0) ? false : this.component;
22971         this.hasInput = this.component && this.inputEl().length;
22972         
22973         if (typeof(this.minViewMode === 'string')) {
22974             switch (this.minViewMode) {
22975                 case 'months':
22976                     this.minViewMode = 1;
22977                     break;
22978                 case 'years':
22979                     this.minViewMode = 2;
22980                     break;
22981                 default:
22982                     this.minViewMode = 0;
22983                     break;
22984             }
22985         }
22986         
22987         if (typeof(this.viewMode === 'string')) {
22988             switch (this.viewMode) {
22989                 case 'months':
22990                     this.viewMode = 1;
22991                     break;
22992                 case 'years':
22993                     this.viewMode = 2;
22994                     break;
22995                 default:
22996                     this.viewMode = 0;
22997                     break;
22998             }
22999         }
23000                 
23001         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23002         
23003 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23004         
23005         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23006         
23007         this.picker().on('mousedown', this.onMousedown, this);
23008         this.picker().on('click', this.onClick, this);
23009         
23010         this.picker().addClass('datepicker-dropdown');
23011         
23012         this.startViewMode = this.viewMode;
23013         
23014         if(this.singleMode){
23015             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23016                 v.setVisibilityMode(Roo.Element.DISPLAY);
23017                 v.hide();
23018             });
23019             
23020             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23021                 v.setStyle('width', '189px');
23022             });
23023         }
23024         
23025         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23026             if(!this.calendarWeeks){
23027                 v.remove();
23028                 return;
23029             }
23030             
23031             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23032             v.attr('colspan', function(i, val){
23033                 return parseInt(val) + 1;
23034             });
23035         });
23036                         
23037         
23038         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23039         
23040         this.setStartDate(this.startDate);
23041         this.setEndDate(this.endDate);
23042         
23043         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23044         
23045         this.fillDow();
23046         this.fillMonths();
23047         this.update();
23048         this.showMode();
23049         
23050         if(this.isInline) {
23051             this.showPopup();
23052         }
23053     },
23054     
23055     picker : function()
23056     {
23057         return this.pickerEl;
23058 //        return this.el.select('.datepicker', true).first();
23059     },
23060     
23061     fillDow: function()
23062     {
23063         var dowCnt = this.weekStart;
23064         
23065         var dow = {
23066             tag: 'tr',
23067             cn: [
23068                 
23069             ]
23070         };
23071         
23072         if(this.calendarWeeks){
23073             dow.cn.push({
23074                 tag: 'th',
23075                 cls: 'cw',
23076                 html: '&nbsp;'
23077             })
23078         }
23079         
23080         while (dowCnt < this.weekStart + 7) {
23081             dow.cn.push({
23082                 tag: 'th',
23083                 cls: 'dow',
23084                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23085             });
23086         }
23087         
23088         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23089     },
23090     
23091     fillMonths: function()
23092     {    
23093         var i = 0;
23094         var months = this.picker().select('>.datepicker-months td', true).first();
23095         
23096         months.dom.innerHTML = '';
23097         
23098         while (i < 12) {
23099             var month = {
23100                 tag: 'span',
23101                 cls: 'month',
23102                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23103             };
23104             
23105             months.createChild(month);
23106         }
23107         
23108     },
23109     
23110     update: function()
23111     {
23112         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;
23113         
23114         if (this.date < this.startDate) {
23115             this.viewDate = new Date(this.startDate);
23116         } else if (this.date > this.endDate) {
23117             this.viewDate = new Date(this.endDate);
23118         } else {
23119             this.viewDate = new Date(this.date);
23120         }
23121         
23122         this.fill();
23123     },
23124     
23125     fill: function() 
23126     {
23127         var d = new Date(this.viewDate),
23128                 year = d.getUTCFullYear(),
23129                 month = d.getUTCMonth(),
23130                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23131                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23132                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23133                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23134                 currentDate = this.date && this.date.valueOf(),
23135                 today = this.UTCToday();
23136         
23137         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23138         
23139 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23140         
23141 //        this.picker.select('>tfoot th.today').
23142 //                                              .text(dates[this.language].today)
23143 //                                              .toggle(this.todayBtn !== false);
23144     
23145         this.updateNavArrows();
23146         this.fillMonths();
23147                                                 
23148         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23149         
23150         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23151          
23152         prevMonth.setUTCDate(day);
23153         
23154         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23155         
23156         var nextMonth = new Date(prevMonth);
23157         
23158         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23159         
23160         nextMonth = nextMonth.valueOf();
23161         
23162         var fillMonths = false;
23163         
23164         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23165         
23166         while(prevMonth.valueOf() <= nextMonth) {
23167             var clsName = '';
23168             
23169             if (prevMonth.getUTCDay() === this.weekStart) {
23170                 if(fillMonths){
23171                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23172                 }
23173                     
23174                 fillMonths = {
23175                     tag: 'tr',
23176                     cn: []
23177                 };
23178                 
23179                 if(this.calendarWeeks){
23180                     // ISO 8601: First week contains first thursday.
23181                     // ISO also states week starts on Monday, but we can be more abstract here.
23182                     var
23183                     // Start of current week: based on weekstart/current date
23184                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23185                     // Thursday of this week
23186                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23187                     // First Thursday of year, year from thursday
23188                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23189                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23190                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23191                     
23192                     fillMonths.cn.push({
23193                         tag: 'td',
23194                         cls: 'cw',
23195                         html: calWeek
23196                     });
23197                 }
23198             }
23199             
23200             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23201                 clsName += ' old';
23202             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23203                 clsName += ' new';
23204             }
23205             if (this.todayHighlight &&
23206                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23207                 prevMonth.getUTCMonth() == today.getMonth() &&
23208                 prevMonth.getUTCDate() == today.getDate()) {
23209                 clsName += ' today';
23210             }
23211             
23212             if (currentDate && prevMonth.valueOf() === currentDate) {
23213                 clsName += ' active';
23214             }
23215             
23216             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23217                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23218                     clsName += ' disabled';
23219             }
23220             
23221             fillMonths.cn.push({
23222                 tag: 'td',
23223                 cls: 'day ' + clsName,
23224                 html: prevMonth.getDate()
23225             });
23226             
23227             prevMonth.setDate(prevMonth.getDate()+1);
23228         }
23229           
23230         var currentYear = this.date && this.date.getUTCFullYear();
23231         var currentMonth = this.date && this.date.getUTCMonth();
23232         
23233         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23234         
23235         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23236             v.removeClass('active');
23237             
23238             if(currentYear === year && k === currentMonth){
23239                 v.addClass('active');
23240             }
23241             
23242             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23243                 v.addClass('disabled');
23244             }
23245             
23246         });
23247         
23248         
23249         year = parseInt(year/10, 10) * 10;
23250         
23251         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23252         
23253         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23254         
23255         year -= 1;
23256         for (var i = -1; i < 11; i++) {
23257             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23258                 tag: 'span',
23259                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23260                 html: year
23261             });
23262             
23263             year += 1;
23264         }
23265     },
23266     
23267     showMode: function(dir) 
23268     {
23269         if (dir) {
23270             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23271         }
23272         
23273         Roo.each(this.picker().select('>div',true).elements, function(v){
23274             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23275             v.hide();
23276         });
23277         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23278     },
23279     
23280     place: function()
23281     {
23282         if(this.isInline) {
23283             return;
23284         }
23285         
23286         this.picker().removeClass(['bottom', 'top']);
23287         
23288         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23289             /*
23290              * place to the top of element!
23291              *
23292              */
23293             
23294             this.picker().addClass('top');
23295             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23296             
23297             return;
23298         }
23299         
23300         this.picker().addClass('bottom');
23301         
23302         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23303     },
23304     
23305     parseDate : function(value)
23306     {
23307         if(!value || value instanceof Date){
23308             return value;
23309         }
23310         var v = Date.parseDate(value, this.format);
23311         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23312             v = Date.parseDate(value, 'Y-m-d');
23313         }
23314         if(!v && this.altFormats){
23315             if(!this.altFormatsArray){
23316                 this.altFormatsArray = this.altFormats.split("|");
23317             }
23318             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23319                 v = Date.parseDate(value, this.altFormatsArray[i]);
23320             }
23321         }
23322         return v;
23323     },
23324     
23325     formatDate : function(date, fmt)
23326     {   
23327         return (!date || !(date instanceof Date)) ?
23328         date : date.dateFormat(fmt || this.format);
23329     },
23330     
23331     onFocus : function()
23332     {
23333         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23334         this.showPopup();
23335     },
23336     
23337     onBlur : function()
23338     {
23339         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23340         
23341         var d = this.inputEl().getValue();
23342         
23343         this.setValue(d);
23344                 
23345         this.hidePopup();
23346     },
23347     
23348     showPopup : function()
23349     {
23350         this.picker().show();
23351         this.update();
23352         this.place();
23353         
23354         this.fireEvent('showpopup', this, this.date);
23355     },
23356     
23357     hidePopup : function()
23358     {
23359         if(this.isInline) {
23360             return;
23361         }
23362         this.picker().hide();
23363         this.viewMode = this.startViewMode;
23364         this.showMode();
23365         
23366         this.fireEvent('hidepopup', this, this.date);
23367         
23368     },
23369     
23370     onMousedown: function(e)
23371     {
23372         e.stopPropagation();
23373         e.preventDefault();
23374     },
23375     
23376     keyup: function(e)
23377     {
23378         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23379         this.update();
23380     },
23381
23382     setValue: function(v)
23383     {
23384         if(this.fireEvent('beforeselect', this, v) !== false){
23385             var d = new Date(this.parseDate(v) ).clearTime();
23386         
23387             if(isNaN(d.getTime())){
23388                 this.date = this.viewDate = '';
23389                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23390                 return;
23391             }
23392
23393             v = this.formatDate(d);
23394
23395             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23396
23397             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23398
23399             this.update();
23400
23401             this.fireEvent('select', this, this.date);
23402         }
23403     },
23404     
23405     getValue: function()
23406     {
23407         return this.formatDate(this.date);
23408     },
23409     
23410     fireKey: function(e)
23411     {
23412         if (!this.picker().isVisible()){
23413             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23414                 this.showPopup();
23415             }
23416             return;
23417         }
23418         
23419         var dateChanged = false,
23420         dir, day, month,
23421         newDate, newViewDate;
23422         
23423         switch(e.keyCode){
23424             case 27: // escape
23425                 this.hidePopup();
23426                 e.preventDefault();
23427                 break;
23428             case 37: // left
23429             case 39: // right
23430                 if (!this.keyboardNavigation) {
23431                     break;
23432                 }
23433                 dir = e.keyCode == 37 ? -1 : 1;
23434                 
23435                 if (e.ctrlKey){
23436                     newDate = this.moveYear(this.date, dir);
23437                     newViewDate = this.moveYear(this.viewDate, dir);
23438                 } else if (e.shiftKey){
23439                     newDate = this.moveMonth(this.date, dir);
23440                     newViewDate = this.moveMonth(this.viewDate, dir);
23441                 } else {
23442                     newDate = new Date(this.date);
23443                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23444                     newViewDate = new Date(this.viewDate);
23445                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23446                 }
23447                 if (this.dateWithinRange(newDate)){
23448                     this.date = newDate;
23449                     this.viewDate = newViewDate;
23450                     this.setValue(this.formatDate(this.date));
23451 //                    this.update();
23452                     e.preventDefault();
23453                     dateChanged = true;
23454                 }
23455                 break;
23456             case 38: // up
23457             case 40: // down
23458                 if (!this.keyboardNavigation) {
23459                     break;
23460                 }
23461                 dir = e.keyCode == 38 ? -1 : 1;
23462                 if (e.ctrlKey){
23463                     newDate = this.moveYear(this.date, dir);
23464                     newViewDate = this.moveYear(this.viewDate, dir);
23465                 } else if (e.shiftKey){
23466                     newDate = this.moveMonth(this.date, dir);
23467                     newViewDate = this.moveMonth(this.viewDate, dir);
23468                 } else {
23469                     newDate = new Date(this.date);
23470                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23471                     newViewDate = new Date(this.viewDate);
23472                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23473                 }
23474                 if (this.dateWithinRange(newDate)){
23475                     this.date = newDate;
23476                     this.viewDate = newViewDate;
23477                     this.setValue(this.formatDate(this.date));
23478 //                    this.update();
23479                     e.preventDefault();
23480                     dateChanged = true;
23481                 }
23482                 break;
23483             case 13: // enter
23484                 this.setValue(this.formatDate(this.date));
23485                 this.hidePopup();
23486                 e.preventDefault();
23487                 break;
23488             case 9: // tab
23489                 this.setValue(this.formatDate(this.date));
23490                 this.hidePopup();
23491                 break;
23492             case 16: // shift
23493             case 17: // ctrl
23494             case 18: // alt
23495                 break;
23496             default :
23497                 this.hidePopup();
23498                 
23499         }
23500     },
23501     
23502     
23503     onClick: function(e) 
23504     {
23505         e.stopPropagation();
23506         e.preventDefault();
23507         
23508         var target = e.getTarget();
23509         
23510         if(target.nodeName.toLowerCase() === 'i'){
23511             target = Roo.get(target).dom.parentNode;
23512         }
23513         
23514         var nodeName = target.nodeName;
23515         var className = target.className;
23516         var html = target.innerHTML;
23517         //Roo.log(nodeName);
23518         
23519         switch(nodeName.toLowerCase()) {
23520             case 'th':
23521                 switch(className) {
23522                     case 'switch':
23523                         this.showMode(1);
23524                         break;
23525                     case 'prev':
23526                     case 'next':
23527                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23528                         switch(this.viewMode){
23529                                 case 0:
23530                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23531                                         break;
23532                                 case 1:
23533                                 case 2:
23534                                         this.viewDate = this.moveYear(this.viewDate, dir);
23535                                         break;
23536                         }
23537                         this.fill();
23538                         break;
23539                     case 'today':
23540                         var date = new Date();
23541                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23542 //                        this.fill()
23543                         this.setValue(this.formatDate(this.date));
23544                         
23545                         this.hidePopup();
23546                         break;
23547                 }
23548                 break;
23549             case 'span':
23550                 if (className.indexOf('disabled') < 0) {
23551                 if (!this.viewDate) {
23552                     this.viewDate = new Date();
23553                 }
23554                 this.viewDate.setUTCDate(1);
23555                     if (className.indexOf('month') > -1) {
23556                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23557                     } else {
23558                         var year = parseInt(html, 10) || 0;
23559                         this.viewDate.setUTCFullYear(year);
23560                         
23561                     }
23562                     
23563                     if(this.singleMode){
23564                         this.setValue(this.formatDate(this.viewDate));
23565                         this.hidePopup();
23566                         return;
23567                     }
23568                     
23569                     this.showMode(-1);
23570                     this.fill();
23571                 }
23572                 break;
23573                 
23574             case 'td':
23575                 //Roo.log(className);
23576                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23577                     var day = parseInt(html, 10) || 1;
23578                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23579                         month = (this.viewDate || new Date()).getUTCMonth();
23580
23581                     if (className.indexOf('old') > -1) {
23582                         if(month === 0 ){
23583                             month = 11;
23584                             year -= 1;
23585                         }else{
23586                             month -= 1;
23587                         }
23588                     } else if (className.indexOf('new') > -1) {
23589                         if (month == 11) {
23590                             month = 0;
23591                             year += 1;
23592                         } else {
23593                             month += 1;
23594                         }
23595                     }
23596                     //Roo.log([year,month,day]);
23597                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23598                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23599 //                    this.fill();
23600                     //Roo.log(this.formatDate(this.date));
23601                     this.setValue(this.formatDate(this.date));
23602                     this.hidePopup();
23603                 }
23604                 break;
23605         }
23606     },
23607     
23608     setStartDate: function(startDate)
23609     {
23610         this.startDate = startDate || -Infinity;
23611         if (this.startDate !== -Infinity) {
23612             this.startDate = this.parseDate(this.startDate);
23613         }
23614         this.update();
23615         this.updateNavArrows();
23616     },
23617
23618     setEndDate: function(endDate)
23619     {
23620         this.endDate = endDate || Infinity;
23621         if (this.endDate !== Infinity) {
23622             this.endDate = this.parseDate(this.endDate);
23623         }
23624         this.update();
23625         this.updateNavArrows();
23626     },
23627     
23628     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23629     {
23630         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23631         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23632             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23633         }
23634         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23635             return parseInt(d, 10);
23636         });
23637         this.update();
23638         this.updateNavArrows();
23639     },
23640     
23641     updateNavArrows: function() 
23642     {
23643         if(this.singleMode){
23644             return;
23645         }
23646         
23647         var d = new Date(this.viewDate),
23648         year = d.getUTCFullYear(),
23649         month = d.getUTCMonth();
23650         
23651         Roo.each(this.picker().select('.prev', true).elements, function(v){
23652             v.show();
23653             switch (this.viewMode) {
23654                 case 0:
23655
23656                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23657                         v.hide();
23658                     }
23659                     break;
23660                 case 1:
23661                 case 2:
23662                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23663                         v.hide();
23664                     }
23665                     break;
23666             }
23667         });
23668         
23669         Roo.each(this.picker().select('.next', true).elements, function(v){
23670             v.show();
23671             switch (this.viewMode) {
23672                 case 0:
23673
23674                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23675                         v.hide();
23676                     }
23677                     break;
23678                 case 1:
23679                 case 2:
23680                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23681                         v.hide();
23682                     }
23683                     break;
23684             }
23685         })
23686     },
23687     
23688     moveMonth: function(date, dir)
23689     {
23690         if (!dir) {
23691             return date;
23692         }
23693         var new_date = new Date(date.valueOf()),
23694         day = new_date.getUTCDate(),
23695         month = new_date.getUTCMonth(),
23696         mag = Math.abs(dir),
23697         new_month, test;
23698         dir = dir > 0 ? 1 : -1;
23699         if (mag == 1){
23700             test = dir == -1
23701             // If going back one month, make sure month is not current month
23702             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23703             ? function(){
23704                 return new_date.getUTCMonth() == month;
23705             }
23706             // If going forward one month, make sure month is as expected
23707             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23708             : function(){
23709                 return new_date.getUTCMonth() != new_month;
23710             };
23711             new_month = month + dir;
23712             new_date.setUTCMonth(new_month);
23713             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23714             if (new_month < 0 || new_month > 11) {
23715                 new_month = (new_month + 12) % 12;
23716             }
23717         } else {
23718             // For magnitudes >1, move one month at a time...
23719             for (var i=0; i<mag; i++) {
23720                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23721                 new_date = this.moveMonth(new_date, dir);
23722             }
23723             // ...then reset the day, keeping it in the new month
23724             new_month = new_date.getUTCMonth();
23725             new_date.setUTCDate(day);
23726             test = function(){
23727                 return new_month != new_date.getUTCMonth();
23728             };
23729         }
23730         // Common date-resetting loop -- if date is beyond end of month, make it
23731         // end of month
23732         while (test()){
23733             new_date.setUTCDate(--day);
23734             new_date.setUTCMonth(new_month);
23735         }
23736         return new_date;
23737     },
23738
23739     moveYear: function(date, dir)
23740     {
23741         return this.moveMonth(date, dir*12);
23742     },
23743
23744     dateWithinRange: function(date)
23745     {
23746         return date >= this.startDate && date <= this.endDate;
23747     },
23748
23749     
23750     remove: function() 
23751     {
23752         this.picker().remove();
23753     },
23754     
23755     validateValue : function(value)
23756     {
23757         if(this.getVisibilityEl().hasClass('hidden')){
23758             return true;
23759         }
23760         
23761         if(value.length < 1)  {
23762             if(this.allowBlank){
23763                 return true;
23764             }
23765             return false;
23766         }
23767         
23768         if(value.length < this.minLength){
23769             return false;
23770         }
23771         if(value.length > this.maxLength){
23772             return false;
23773         }
23774         if(this.vtype){
23775             var vt = Roo.form.VTypes;
23776             if(!vt[this.vtype](value, this)){
23777                 return false;
23778             }
23779         }
23780         if(typeof this.validator == "function"){
23781             var msg = this.validator(value);
23782             if(msg !== true){
23783                 return false;
23784             }
23785         }
23786         
23787         if(this.regex && !this.regex.test(value)){
23788             return false;
23789         }
23790         
23791         if(typeof(this.parseDate(value)) == 'undefined'){
23792             return false;
23793         }
23794         
23795         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23796             return false;
23797         }      
23798         
23799         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23800             return false;
23801         } 
23802         
23803         
23804         return true;
23805     },
23806     
23807     reset : function()
23808     {
23809         this.date = this.viewDate = '';
23810         
23811         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23812     }
23813    
23814 });
23815
23816 Roo.apply(Roo.bootstrap.form.DateField,  {
23817     
23818     head : {
23819         tag: 'thead',
23820         cn: [
23821         {
23822             tag: 'tr',
23823             cn: [
23824             {
23825                 tag: 'th',
23826                 cls: 'prev',
23827                 html: '<i class="fa fa-arrow-left"/>'
23828             },
23829             {
23830                 tag: 'th',
23831                 cls: 'switch',
23832                 colspan: '5'
23833             },
23834             {
23835                 tag: 'th',
23836                 cls: 'next',
23837                 html: '<i class="fa fa-arrow-right"/>'
23838             }
23839
23840             ]
23841         }
23842         ]
23843     },
23844     
23845     content : {
23846         tag: 'tbody',
23847         cn: [
23848         {
23849             tag: 'tr',
23850             cn: [
23851             {
23852                 tag: 'td',
23853                 colspan: '7'
23854             }
23855             ]
23856         }
23857         ]
23858     },
23859     
23860     footer : {
23861         tag: 'tfoot',
23862         cn: [
23863         {
23864             tag: 'tr',
23865             cn: [
23866             {
23867                 tag: 'th',
23868                 colspan: '7',
23869                 cls: 'today'
23870             }
23871                     
23872             ]
23873         }
23874         ]
23875     },
23876     
23877     dates:{
23878         en: {
23879             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23880             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23881             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23882             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23883             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23884             today: "Today"
23885         }
23886     },
23887     
23888     modes: [
23889     {
23890         clsName: 'days',
23891         navFnc: 'Month',
23892         navStep: 1
23893     },
23894     {
23895         clsName: 'months',
23896         navFnc: 'FullYear',
23897         navStep: 1
23898     },
23899     {
23900         clsName: 'years',
23901         navFnc: 'FullYear',
23902         navStep: 10
23903     }]
23904 });
23905
23906 Roo.apply(Roo.bootstrap.form.DateField,  {
23907   
23908     template : {
23909         tag: 'div',
23910         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23911         cn: [
23912         {
23913             tag: 'div',
23914             cls: 'datepicker-days',
23915             cn: [
23916             {
23917                 tag: 'table',
23918                 cls: 'table-condensed',
23919                 cn:[
23920                 Roo.bootstrap.form.DateField.head,
23921                 {
23922                     tag: 'tbody'
23923                 },
23924                 Roo.bootstrap.form.DateField.footer
23925                 ]
23926             }
23927             ]
23928         },
23929         {
23930             tag: 'div',
23931             cls: 'datepicker-months',
23932             cn: [
23933             {
23934                 tag: 'table',
23935                 cls: 'table-condensed',
23936                 cn:[
23937                 Roo.bootstrap.form.DateField.head,
23938                 Roo.bootstrap.form.DateField.content,
23939                 Roo.bootstrap.form.DateField.footer
23940                 ]
23941             }
23942             ]
23943         },
23944         {
23945             tag: 'div',
23946             cls: 'datepicker-years',
23947             cn: [
23948             {
23949                 tag: 'table',
23950                 cls: 'table-condensed',
23951                 cn:[
23952                 Roo.bootstrap.form.DateField.head,
23953                 Roo.bootstrap.form.DateField.content,
23954                 Roo.bootstrap.form.DateField.footer
23955                 ]
23956             }
23957             ]
23958         }
23959         ]
23960     }
23961 });
23962
23963  
23964
23965  /*
23966  * - LGPL
23967  *
23968  * TimeField
23969  * 
23970  */
23971
23972 /**
23973  * @class Roo.bootstrap.form.TimeField
23974  * @extends Roo.bootstrap.form.Input
23975  * Bootstrap DateField class
23976  * @cfg {Number} minuteStep the minutes goes up/down by a fixed number, default 1
23977  * 
23978  * 
23979  * @constructor
23980  * Create a new TimeField
23981  * @param {Object} config The config object
23982  */
23983
23984 Roo.bootstrap.form.TimeField = function(config){
23985     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23986     this.addEvents({
23987             /**
23988              * @event show
23989              * Fires when this field show.
23990              * @param {Roo.bootstrap.form.DateField} thisthis
23991              * @param {Mixed} date The date value
23992              */
23993             show : true,
23994             /**
23995              * @event show
23996              * Fires when this field hide.
23997              * @param {Roo.bootstrap.form.DateField} this
23998              * @param {Mixed} date The date value
23999              */
24000             hide : true,
24001             /**
24002              * @event select
24003              * Fires when select a date.
24004              * @param {Roo.bootstrap.form.DateField} this
24005              * @param {Mixed} date The date value
24006              */
24007             select : true
24008         });
24009 };
24010
24011 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24012     
24013     /**
24014      * @cfg {String} format
24015      * The default time format string which can be overriden for localization support.  The format must be
24016      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24017      */
24018     format : "H:i",
24019     minuteStep : 1,
24020
24021     getAutoCreate : function()
24022     {
24023         this.after = '<i class="fa far fa-clock"></i>';
24024         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24025         
24026          
24027     },
24028     onRender: function(ct, position)
24029     {
24030         
24031         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24032                 
24033         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24034         
24035         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24036         
24037         this.pop = this.picker().select('>.datepicker-time',true).first();
24038         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24039         
24040         this.picker().on('mousedown', this.onMousedown, this);
24041         this.picker().on('click', this.onClick, this);
24042         
24043         this.picker().addClass('datepicker-dropdown');
24044     
24045         this.fillTime();
24046         this.update();
24047             
24048         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24049         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24050         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24051         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24052         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24053         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24054
24055     },
24056     
24057     fireKey: function(e){
24058         if (!this.picker().isVisible()){
24059             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24060                 this.show();
24061             }
24062             return;
24063         }
24064
24065         e.preventDefault();
24066         
24067         switch(e.keyCode){
24068             case 27: // escape
24069                 this.hide();
24070                 break;
24071             case 37: // left
24072             case 39: // right
24073                 this.onTogglePeriod();
24074                 break;
24075             case 38: // up
24076                 this.onIncrementMinutes();
24077                 break;
24078             case 40: // down
24079                 this.onDecrementMinutes();
24080                 break;
24081             case 13: // enter
24082             case 9: // tab
24083                 this.setTime();
24084                 break;
24085         }
24086     },
24087     
24088     onClick: function(e) {
24089         e.stopPropagation();
24090         e.preventDefault();
24091     },
24092     
24093     picker : function()
24094     {
24095         return this.pickerEl;
24096     },
24097     
24098     fillTime: function()
24099     {    
24100         var time = this.pop.select('tbody', true).first();
24101         
24102         time.dom.innerHTML = '';
24103         
24104         time.createChild({
24105             tag: 'tr',
24106             cn: [
24107                 {
24108                     tag: 'td',
24109                     cn: [
24110                         {
24111                             tag: 'a',
24112                             href: '#',
24113                             cls: 'btn',
24114                             cn: [
24115                                 {
24116                                     tag: 'i',
24117                                     cls: 'hours-up fa fas fa-chevron-up'
24118                                 }
24119                             ]
24120                         } 
24121                     ]
24122                 },
24123                 {
24124                     tag: 'td',
24125                     cls: 'separator'
24126                 },
24127                 {
24128                     tag: 'td',
24129                     cn: [
24130                         {
24131                             tag: 'a',
24132                             href: '#',
24133                             cls: 'btn',
24134                             cn: [
24135                                 {
24136                                     tag: 'i',
24137                                     cls: 'minutes-up fa fas fa-chevron-up'
24138                                 }
24139                             ]
24140                         }
24141                     ]
24142                 },
24143                 {
24144                     tag: 'td',
24145                     cls: 'separator'
24146                 }
24147             ]
24148         });
24149         
24150         time.createChild({
24151             tag: 'tr',
24152             cn: [
24153                 {
24154                     tag: 'td',
24155                     cn: [
24156                         {
24157                             tag: 'span',
24158                             cls: 'timepicker-hour',
24159                             html: '00'
24160                         }  
24161                     ]
24162                 },
24163                 {
24164                     tag: 'td',
24165                     cls: 'separator',
24166                     html: ':'
24167                 },
24168                 {
24169                     tag: 'td',
24170                     cn: [
24171                         {
24172                             tag: 'span',
24173                             cls: 'timepicker-minute',
24174                             html: '00'
24175                         }  
24176                     ]
24177                 },
24178                 {
24179                     tag: 'td',
24180                     cls: 'separator'
24181                 },
24182                 {
24183                     tag: 'td',
24184                     cn: [
24185                         {
24186                             tag: 'button',
24187                             type: 'button',
24188                             cls: 'btn btn-primary period',
24189                             html: 'AM'
24190                             
24191                         }
24192                     ]
24193                 }
24194             ]
24195         });
24196         
24197         time.createChild({
24198             tag: 'tr',
24199             cn: [
24200                 {
24201                     tag: 'td',
24202                     cn: [
24203                         {
24204                             tag: 'a',
24205                             href: '#',
24206                             cls: 'btn',
24207                             cn: [
24208                                 {
24209                                     tag: 'span',
24210                                     cls: 'hours-down fa fas fa-chevron-down'
24211                                 }
24212                             ]
24213                         }
24214                     ]
24215                 },
24216                 {
24217                     tag: 'td',
24218                     cls: 'separator'
24219                 },
24220                 {
24221                     tag: 'td',
24222                     cn: [
24223                         {
24224                             tag: 'a',
24225                             href: '#',
24226                             cls: 'btn',
24227                             cn: [
24228                                 {
24229                                     tag: 'span',
24230                                     cls: 'minutes-down fa fas fa-chevron-down'
24231                                 }
24232                             ]
24233                         }
24234                     ]
24235                 },
24236                 {
24237                     tag: 'td',
24238                     cls: 'separator'
24239                 }
24240             ]
24241         });
24242         
24243     },
24244     
24245     update: function()
24246     {
24247         
24248         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24249         
24250         this.fill();
24251     },
24252     
24253     fill: function() 
24254     {
24255         var hours = this.time.getHours();
24256         var minutes = this.time.getMinutes();
24257         var period = 'AM';
24258         
24259         if(hours > 11){
24260             period = 'PM';
24261         }
24262         
24263         if(hours == 0){
24264             hours = 12;
24265         }
24266         
24267         
24268         if(hours > 12){
24269             hours = hours - 12;
24270         }
24271         
24272         if(hours < 10){
24273             hours = '0' + hours;
24274         }
24275         
24276         if(minutes < 10){
24277             minutes = '0' + minutes;
24278         }
24279         
24280         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24281         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24282         this.pop.select('button', true).first().dom.innerHTML = period;
24283         
24284     },
24285     
24286     place: function()
24287     {   
24288         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24289         
24290         var cls = ['bottom'];
24291         
24292         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24293             cls.pop();
24294             cls.push('top');
24295         }
24296         
24297         cls.push('right');
24298         
24299         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24300             cls.pop();
24301             cls.push('left');
24302         }
24303         //this.picker().setXY(20000,20000);
24304         this.picker().addClass(cls.join('-'));
24305         
24306         var _this = this;
24307         
24308         Roo.each(cls, function(c){
24309             if(c == 'bottom'){
24310                 (function() {
24311                  //  
24312                 }).defer(200);
24313                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24314                 //_this.picker().setTop(_this.inputEl().getHeight());
24315                 return;
24316             }
24317             if(c == 'top'){
24318                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24319                 
24320                 //_this.picker().setTop(0 - _this.picker().getHeight());
24321                 return;
24322             }
24323             /*
24324             if(c == 'left'){
24325                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24326                 return;
24327             }
24328             if(c == 'right'){
24329                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24330                 return;
24331             }
24332             */
24333         });
24334         
24335     },
24336   
24337     onFocus : function()
24338     {
24339         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24340         this.show();
24341     },
24342     
24343     onBlur : function()
24344     {
24345         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24346         this.hide();
24347     },
24348     
24349     show : function()
24350     {
24351         this.picker().show();
24352         this.pop.show();
24353         this.update();
24354         this.place();
24355         
24356         this.fireEvent('show', this, this.date);
24357     },
24358     
24359     hide : function()
24360     {
24361         this.picker().hide();
24362         this.pop.hide();
24363         
24364         this.fireEvent('hide', this, this.date);
24365     },
24366     
24367     setTime : function()
24368     {
24369         this.hide();
24370         this.setValue(this.time.format(this.format));
24371         
24372         this.fireEvent('select', this, this.date);
24373         
24374         
24375     },
24376     
24377     onMousedown: function(e){
24378         e.stopPropagation();
24379         e.preventDefault();
24380     },
24381     
24382     onIncrementHours: function()
24383     {
24384         Roo.log('onIncrementHours');
24385         this.time = this.time.add(Date.HOUR, 1);
24386         this.update();
24387         
24388     },
24389     
24390     onDecrementHours: function()
24391     {
24392         Roo.log('onDecrementHours');
24393         this.time = this.time.add(Date.HOUR, -1);
24394         this.update();
24395     },
24396     
24397     onIncrementMinutes: function()
24398     {
24399         Roo.log('onIncrementMinutes');
24400         Roo.log(this.time);
24401         Roo.log(this.time.format('i'));
24402         Roo.log(Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep);
24403         Roo.log(Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24404         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24405         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24406         this.update();
24407     },
24408     
24409     onDecrementMinutes: function()
24410     {
24411         Roo.log('onDecrementMinutes');
24412         Roo.log(this.time);
24413         Roo.log(this.time.format('i'));
24414         Roo.log(Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep);
24415         Roo.log(parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep);
24416         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep);
24417         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24418         this.update();
24419     },
24420     
24421     onTogglePeriod: function()
24422     {
24423         Roo.log('onTogglePeriod');
24424         this.time = this.time.add(Date.HOUR, 12);
24425         this.update();
24426     }
24427     
24428    
24429 });
24430  
24431
24432 Roo.apply(Roo.bootstrap.form.TimeField,  {
24433   
24434     template : {
24435         tag: 'div',
24436         cls: 'datepicker dropdown-menu',
24437         cn: [
24438             {
24439                 tag: 'div',
24440                 cls: 'datepicker-time',
24441                 cn: [
24442                 {
24443                     tag: 'table',
24444                     cls: 'table-condensed',
24445                     cn:[
24446                         {
24447                             tag: 'tbody',
24448                             cn: [
24449                                 {
24450                                     tag: 'tr',
24451                                     cn: [
24452                                     {
24453                                         tag: 'td',
24454                                         colspan: '7'
24455                                     }
24456                                     ]
24457                                 }
24458                             ]
24459                         },
24460                         {
24461                             tag: 'tfoot',
24462                             cn: [
24463                                 {
24464                                     tag: 'tr',
24465                                     cn: [
24466                                     {
24467                                         tag: 'th',
24468                                         colspan: '7',
24469                                         cls: '',
24470                                         cn: [
24471                                             {
24472                                                 tag: 'button',
24473                                                 cls: 'btn btn-info ok',
24474                                                 html: 'OK'
24475                                             }
24476                                         ]
24477                                     }
24478                     
24479                                     ]
24480                                 }
24481                             ]
24482                         }
24483                     ]
24484                 }
24485                 ]
24486             }
24487         ]
24488     }
24489 });
24490
24491  
24492
24493  /*
24494  * - LGPL
24495  *
24496  * MonthField
24497  * 
24498  */
24499
24500 /**
24501  * @class Roo.bootstrap.form.MonthField
24502  * @extends Roo.bootstrap.form.Input
24503  * Bootstrap MonthField class
24504  * 
24505  * @cfg {String} language default en
24506  * 
24507  * @constructor
24508  * Create a new MonthField
24509  * @param {Object} config The config object
24510  */
24511
24512 Roo.bootstrap.form.MonthField = function(config){
24513     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24514     
24515     this.addEvents({
24516         /**
24517          * @event show
24518          * Fires when this field show.
24519          * @param {Roo.bootstrap.form.MonthField} this
24520          * @param {Mixed} date The date value
24521          */
24522         show : true,
24523         /**
24524          * @event show
24525          * Fires when this field hide.
24526          * @param {Roo.bootstrap.form.MonthField} this
24527          * @param {Mixed} date The date value
24528          */
24529         hide : true,
24530         /**
24531          * @event select
24532          * Fires when select a date.
24533          * @param {Roo.bootstrap.form.MonthField} this
24534          * @param {String} oldvalue The old value
24535          * @param {String} newvalue The new value
24536          */
24537         select : true
24538     });
24539 };
24540
24541 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24542     
24543     onRender: function(ct, position)
24544     {
24545         
24546         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24547         
24548         this.language = this.language || 'en';
24549         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24550         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24551         
24552         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24553         this.isInline = false;
24554         this.isInput = true;
24555         this.component = this.el.select('.add-on', true).first() || false;
24556         this.component = (this.component && this.component.length === 0) ? false : this.component;
24557         this.hasInput = this.component && this.inputEL().length;
24558         
24559         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24560         
24561         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24562         
24563         this.picker().on('mousedown', this.onMousedown, this);
24564         this.picker().on('click', this.onClick, this);
24565         
24566         this.picker().addClass('datepicker-dropdown');
24567         
24568         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24569             v.setStyle('width', '189px');
24570         });
24571         
24572         this.fillMonths();
24573         
24574         this.update();
24575         
24576         if(this.isInline) {
24577             this.show();
24578         }
24579         
24580     },
24581     
24582     setValue: function(v, suppressEvent)
24583     {   
24584         var o = this.getValue();
24585         
24586         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24587         
24588         this.update();
24589
24590         if(suppressEvent !== true){
24591             this.fireEvent('select', this, o, v);
24592         }
24593         
24594     },
24595     
24596     getValue: function()
24597     {
24598         return this.value;
24599     },
24600     
24601     onClick: function(e) 
24602     {
24603         e.stopPropagation();
24604         e.preventDefault();
24605         
24606         var target = e.getTarget();
24607         
24608         if(target.nodeName.toLowerCase() === 'i'){
24609             target = Roo.get(target).dom.parentNode;
24610         }
24611         
24612         var nodeName = target.nodeName;
24613         var className = target.className;
24614         var html = target.innerHTML;
24615         
24616         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24617             return;
24618         }
24619         
24620         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24621         
24622         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24623         
24624         this.hide();
24625                         
24626     },
24627     
24628     picker : function()
24629     {
24630         return this.pickerEl;
24631     },
24632     
24633     fillMonths: function()
24634     {    
24635         var i = 0;
24636         var months = this.picker().select('>.datepicker-months td', true).first();
24637         
24638         months.dom.innerHTML = '';
24639         
24640         while (i < 12) {
24641             var month = {
24642                 tag: 'span',
24643                 cls: 'month',
24644                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24645             };
24646             
24647             months.createChild(month);
24648         }
24649         
24650     },
24651     
24652     update: function()
24653     {
24654         var _this = this;
24655         
24656         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24657             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24658         }
24659         
24660         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24661             e.removeClass('active');
24662             
24663             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24664                 e.addClass('active');
24665             }
24666         })
24667     },
24668     
24669     place: function()
24670     {
24671         if(this.isInline) {
24672             return;
24673         }
24674         
24675         this.picker().removeClass(['bottom', 'top']);
24676         
24677         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24678             /*
24679              * place to the top of element!
24680              *
24681              */
24682             
24683             this.picker().addClass('top');
24684             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24685             
24686             return;
24687         }
24688         
24689         this.picker().addClass('bottom');
24690         
24691         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24692     },
24693     
24694     onFocus : function()
24695     {
24696         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24697         this.show();
24698     },
24699     
24700     onBlur : function()
24701     {
24702         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24703         
24704         var d = this.inputEl().getValue();
24705         
24706         this.setValue(d);
24707                 
24708         this.hide();
24709     },
24710     
24711     show : function()
24712     {
24713         this.picker().show();
24714         this.picker().select('>.datepicker-months', true).first().show();
24715         this.update();
24716         this.place();
24717         
24718         this.fireEvent('show', this, this.date);
24719     },
24720     
24721     hide : function()
24722     {
24723         if(this.isInline) {
24724             return;
24725         }
24726         this.picker().hide();
24727         this.fireEvent('hide', this, this.date);
24728         
24729     },
24730     
24731     onMousedown: function(e)
24732     {
24733         e.stopPropagation();
24734         e.preventDefault();
24735     },
24736     
24737     keyup: function(e)
24738     {
24739         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24740         this.update();
24741     },
24742
24743     fireKey: function(e)
24744     {
24745         if (!this.picker().isVisible()){
24746             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24747                 this.show();
24748             }
24749             return;
24750         }
24751         
24752         var dir;
24753         
24754         switch(e.keyCode){
24755             case 27: // escape
24756                 this.hide();
24757                 e.preventDefault();
24758                 break;
24759             case 37: // left
24760             case 39: // right
24761                 dir = e.keyCode == 37 ? -1 : 1;
24762                 
24763                 this.vIndex = this.vIndex + dir;
24764                 
24765                 if(this.vIndex < 0){
24766                     this.vIndex = 0;
24767                 }
24768                 
24769                 if(this.vIndex > 11){
24770                     this.vIndex = 11;
24771                 }
24772                 
24773                 if(isNaN(this.vIndex)){
24774                     this.vIndex = 0;
24775                 }
24776                 
24777                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24778                 
24779                 break;
24780             case 38: // up
24781             case 40: // down
24782                 
24783                 dir = e.keyCode == 38 ? -1 : 1;
24784                 
24785                 this.vIndex = this.vIndex + dir * 4;
24786                 
24787                 if(this.vIndex < 0){
24788                     this.vIndex = 0;
24789                 }
24790                 
24791                 if(this.vIndex > 11){
24792                     this.vIndex = 11;
24793                 }
24794                 
24795                 if(isNaN(this.vIndex)){
24796                     this.vIndex = 0;
24797                 }
24798                 
24799                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24800                 break;
24801                 
24802             case 13: // enter
24803                 
24804                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24805                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24806                 }
24807                 
24808                 this.hide();
24809                 e.preventDefault();
24810                 break;
24811             case 9: // tab
24812                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24813                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24814                 }
24815                 this.hide();
24816                 break;
24817             case 16: // shift
24818             case 17: // ctrl
24819             case 18: // alt
24820                 break;
24821             default :
24822                 this.hide();
24823                 
24824         }
24825     },
24826     
24827     remove: function() 
24828     {
24829         this.picker().remove();
24830     }
24831    
24832 });
24833
24834 Roo.apply(Roo.bootstrap.form.MonthField,  {
24835     
24836     content : {
24837         tag: 'tbody',
24838         cn: [
24839         {
24840             tag: 'tr',
24841             cn: [
24842             {
24843                 tag: 'td',
24844                 colspan: '7'
24845             }
24846             ]
24847         }
24848         ]
24849     },
24850     
24851     dates:{
24852         en: {
24853             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24854             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24855         }
24856     }
24857 });
24858
24859 Roo.apply(Roo.bootstrap.form.MonthField,  {
24860   
24861     template : {
24862         tag: 'div',
24863         cls: 'datepicker dropdown-menu roo-dynamic',
24864         cn: [
24865             {
24866                 tag: 'div',
24867                 cls: 'datepicker-months',
24868                 cn: [
24869                 {
24870                     tag: 'table',
24871                     cls: 'table-condensed',
24872                     cn:[
24873                         Roo.bootstrap.form.DateField.content
24874                     ]
24875                 }
24876                 ]
24877             }
24878         ]
24879     }
24880 });
24881
24882  
24883
24884  
24885  /*
24886  * - LGPL
24887  *
24888  * CheckBox
24889  * 
24890  */
24891
24892 /**
24893  * @class Roo.bootstrap.form.CheckBox
24894  * @extends Roo.bootstrap.form.Input
24895  * Bootstrap CheckBox class
24896  * 
24897  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24898  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24899  * @cfg {String} boxLabel The text that appears beside the checkbox
24900  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24901  * @cfg {Boolean} checked initnal the element
24902  * @cfg {Boolean} inline inline the element (default false)
24903  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24904  * @cfg {String} tooltip label tooltip
24905  * 
24906  * @constructor
24907  * Create a new CheckBox
24908  * @param {Object} config The config object
24909  */
24910
24911 Roo.bootstrap.form.CheckBox = function(config){
24912     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24913    
24914     this.addEvents({
24915         /**
24916         * @event check
24917         * Fires when the element is checked or unchecked.
24918         * @param {Roo.bootstrap.form.CheckBox} this This input
24919         * @param {Boolean} checked The new checked value
24920         */
24921        check : true,
24922        /**
24923         * @event click
24924         * Fires when the element is click.
24925         * @param {Roo.bootstrap.form.CheckBox} this This input
24926         */
24927        click : true
24928     });
24929     
24930 };
24931
24932 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24933   
24934     inputType: 'checkbox',
24935     inputValue: 1,
24936     valueOff: 0,
24937     boxLabel: false,
24938     checked: false,
24939     weight : false,
24940     inline: false,
24941     tooltip : '',
24942     
24943     // checkbox success does not make any sense really.. 
24944     invalidClass : "",
24945     validClass : "",
24946     
24947     
24948     getAutoCreate : function()
24949     {
24950         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24951         
24952         var id = Roo.id();
24953         
24954         var cfg = {};
24955         
24956         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24957         
24958         if(this.inline){
24959             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24960         }
24961         
24962         var input =  {
24963             tag: 'input',
24964             id : id,
24965             type : this.inputType,
24966             value : this.inputValue,
24967             cls : 'roo-' + this.inputType, //'form-box',
24968             placeholder : this.placeholder || ''
24969             
24970         };
24971         
24972         if(this.inputType != 'radio'){
24973             var hidden =  {
24974                 tag: 'input',
24975                 type : 'hidden',
24976                 cls : 'roo-hidden-value',
24977                 value : this.checked ? this.inputValue : this.valueOff
24978             };
24979         }
24980         
24981             
24982         if (this.weight) { // Validity check?
24983             cfg.cls += " " + this.inputType + "-" + this.weight;
24984         }
24985         
24986         if (this.disabled) {
24987             input.disabled=true;
24988         }
24989         
24990         if(this.checked){
24991             input.checked = this.checked;
24992         }
24993         
24994         if (this.name) {
24995             
24996             input.name = this.name;
24997             
24998             if(this.inputType != 'radio'){
24999                 hidden.name = this.name;
25000                 input.name = '_hidden_' + this.name;
25001             }
25002         }
25003         
25004         if (this.size) {
25005             input.cls += ' input-' + this.size;
25006         }
25007         
25008         var settings=this;
25009         
25010         ['xs','sm','md','lg'].map(function(size){
25011             if (settings[size]) {
25012                 cfg.cls += ' col-' + size + '-' + settings[size];
25013             }
25014         });
25015         
25016         var inputblock = input;
25017          
25018         if (this.before || this.after) {
25019             
25020             inputblock = {
25021                 cls : 'input-group',
25022                 cn :  [] 
25023             };
25024             
25025             if (this.before) {
25026                 inputblock.cn.push({
25027                     tag :'span',
25028                     cls : 'input-group-addon',
25029                     html : this.before
25030                 });
25031             }
25032             
25033             inputblock.cn.push(input);
25034             
25035             if(this.inputType != 'radio'){
25036                 inputblock.cn.push(hidden);
25037             }
25038             
25039             if (this.after) {
25040                 inputblock.cn.push({
25041                     tag :'span',
25042                     cls : 'input-group-addon',
25043                     html : this.after
25044                 });
25045             }
25046             
25047         }
25048         var boxLabelCfg = false;
25049         
25050         if(this.boxLabel){
25051            
25052             boxLabelCfg = {
25053                 tag: 'label',
25054                 //'for': id, // box label is handled by onclick - so no for...
25055                 cls: 'box-label',
25056                 html: this.boxLabel
25057             };
25058             if(this.tooltip){
25059                 boxLabelCfg.tooltip = this.tooltip;
25060             }
25061              
25062         }
25063         
25064         
25065         if (align ==='left' && this.fieldLabel.length) {
25066 //                Roo.log("left and has label");
25067             cfg.cn = [
25068                 {
25069                     tag: 'label',
25070                     'for' :  id,
25071                     cls : 'control-label',
25072                     html : this.fieldLabel
25073                 },
25074                 {
25075                     cls : "", 
25076                     cn: [
25077                         inputblock
25078                     ]
25079                 }
25080             ];
25081             
25082             if (boxLabelCfg) {
25083                 cfg.cn[1].cn.push(boxLabelCfg);
25084             }
25085             
25086             if(this.labelWidth > 12){
25087                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25088             }
25089             
25090             if(this.labelWidth < 13 && this.labelmd == 0){
25091                 this.labelmd = this.labelWidth;
25092             }
25093             
25094             if(this.labellg > 0){
25095                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25096                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25097             }
25098             
25099             if(this.labelmd > 0){
25100                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25101                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25102             }
25103             
25104             if(this.labelsm > 0){
25105                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25106                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25107             }
25108             
25109             if(this.labelxs > 0){
25110                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25111                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25112             }
25113             
25114         } else if ( this.fieldLabel.length) {
25115 //                Roo.log(" label");
25116                 cfg.cn = [
25117                    
25118                     {
25119                         tag: this.boxLabel ? 'span' : 'label',
25120                         'for': id,
25121                         cls: 'control-label box-input-label',
25122                         //cls : 'input-group-addon',
25123                         html : this.fieldLabel
25124                     },
25125                     
25126                     inputblock
25127                     
25128                 ];
25129                 if (boxLabelCfg) {
25130                     cfg.cn.push(boxLabelCfg);
25131                 }
25132
25133         } else {
25134             
25135 //                Roo.log(" no label && no align");
25136                 cfg.cn = [  inputblock ] ;
25137                 if (boxLabelCfg) {
25138                     cfg.cn.push(boxLabelCfg);
25139                 }
25140
25141                 
25142         }
25143         
25144        
25145         
25146         if(this.inputType != 'radio'){
25147             cfg.cn.push(hidden);
25148         }
25149         
25150         return cfg;
25151         
25152     },
25153     
25154     /**
25155      * return the real input element.
25156      */
25157     inputEl: function ()
25158     {
25159         return this.el.select('input.roo-' + this.inputType,true).first();
25160     },
25161     hiddenEl: function ()
25162     {
25163         return this.el.select('input.roo-hidden-value',true).first();
25164     },
25165     
25166     labelEl: function()
25167     {
25168         return this.el.select('label.control-label',true).first();
25169     },
25170     /* depricated... */
25171     
25172     label: function()
25173     {
25174         return this.labelEl();
25175     },
25176     
25177     boxLabelEl: function()
25178     {
25179         return this.el.select('label.box-label',true).first();
25180     },
25181     
25182     initEvents : function()
25183     {
25184 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25185         
25186         this.inputEl().on('click', this.onClick,  this);
25187         
25188         if (this.boxLabel) { 
25189             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25190         }
25191         
25192         this.startValue = this.getValue();
25193         
25194         if(this.groupId){
25195             Roo.bootstrap.form.CheckBox.register(this);
25196         }
25197     },
25198     
25199     onClick : function(e)
25200     {   
25201         if(this.fireEvent('click', this, e) !== false){
25202             this.setChecked(!this.checked);
25203         }
25204         
25205     },
25206     
25207     setChecked : function(state,suppressEvent)
25208     {
25209         this.startValue = this.getValue();
25210
25211         if(this.inputType == 'radio'){
25212             
25213             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25214                 e.dom.checked = false;
25215             });
25216             
25217             this.inputEl().dom.checked = true;
25218             
25219             this.inputEl().dom.value = this.inputValue;
25220             
25221             if(suppressEvent !== true){
25222                 this.fireEvent('check', this, true);
25223             }
25224             
25225             this.validate();
25226             
25227             return;
25228         }
25229         
25230         this.checked = state;
25231         
25232         this.inputEl().dom.checked = state;
25233         
25234         
25235         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25236         
25237         if(suppressEvent !== true){
25238             this.fireEvent('check', this, state);
25239         }
25240         
25241         this.validate();
25242     },
25243     
25244     getValue : function()
25245     {
25246         if(this.inputType == 'radio'){
25247             return this.getGroupValue();
25248         }
25249         
25250         return this.hiddenEl().dom.value;
25251         
25252     },
25253     
25254     getGroupValue : function()
25255     {
25256         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25257             return '';
25258         }
25259         
25260         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25261     },
25262     
25263     setValue : function(v,suppressEvent)
25264     {
25265         if(this.inputType == 'radio'){
25266             this.setGroupValue(v, suppressEvent);
25267             return;
25268         }
25269         
25270         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25271         
25272         this.validate();
25273     },
25274     
25275     setGroupValue : function(v, suppressEvent)
25276     {
25277         this.startValue = this.getValue();
25278         
25279         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25280             e.dom.checked = false;
25281             
25282             if(e.dom.value == v){
25283                 e.dom.checked = true;
25284             }
25285         });
25286         
25287         if(suppressEvent !== true){
25288             this.fireEvent('check', this, true);
25289         }
25290
25291         this.validate();
25292         
25293         return;
25294     },
25295     
25296     validate : function()
25297     {
25298         if(this.getVisibilityEl().hasClass('hidden')){
25299             return true;
25300         }
25301         
25302         if(
25303                 this.disabled || 
25304                 (this.inputType == 'radio' && this.validateRadio()) ||
25305                 (this.inputType == 'checkbox' && this.validateCheckbox())
25306         ){
25307             this.markValid();
25308             return true;
25309         }
25310         
25311         this.markInvalid();
25312         return false;
25313     },
25314     
25315     validateRadio : function()
25316     {
25317         if(this.getVisibilityEl().hasClass('hidden')){
25318             return true;
25319         }
25320         
25321         if(this.allowBlank){
25322             return true;
25323         }
25324         
25325         var valid = false;
25326         
25327         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25328             if(!e.dom.checked){
25329                 return;
25330             }
25331             
25332             valid = true;
25333             
25334             return false;
25335         });
25336         
25337         return valid;
25338     },
25339     
25340     validateCheckbox : function()
25341     {
25342         if(!this.groupId){
25343             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25344             //return (this.getValue() == this.inputValue) ? true : false;
25345         }
25346         
25347         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25348         
25349         if(!group){
25350             return false;
25351         }
25352         
25353         var r = false;
25354         
25355         for(var i in group){
25356             if(group[i].el.isVisible(true)){
25357                 r = false;
25358                 break;
25359             }
25360             
25361             r = true;
25362         }
25363         
25364         for(var i in group){
25365             if(r){
25366                 break;
25367             }
25368             
25369             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25370         }
25371         
25372         return r;
25373     },
25374     
25375     /**
25376      * Mark this field as valid
25377      */
25378     markValid : function()
25379     {
25380         var _this = this;
25381         
25382         this.fireEvent('valid', this);
25383         
25384         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25385         
25386         if(this.groupId){
25387             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25388         }
25389         
25390         if(label){
25391             label.markValid();
25392         }
25393
25394         if(this.inputType == 'radio'){
25395             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25396                 var fg = e.findParent('.form-group', false, true);
25397                 if (Roo.bootstrap.version == 3) {
25398                     fg.removeClass([_this.invalidClass, _this.validClass]);
25399                     fg.addClass(_this.validClass);
25400                 } else {
25401                     fg.removeClass(['is-valid', 'is-invalid']);
25402                     fg.addClass('is-valid');
25403                 }
25404             });
25405             
25406             return;
25407         }
25408
25409         if(!this.groupId){
25410             var fg = this.el.findParent('.form-group', false, true);
25411             if (Roo.bootstrap.version == 3) {
25412                 fg.removeClass([this.invalidClass, this.validClass]);
25413                 fg.addClass(this.validClass);
25414             } else {
25415                 fg.removeClass(['is-valid', 'is-invalid']);
25416                 fg.addClass('is-valid');
25417             }
25418             return;
25419         }
25420         
25421         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25422         
25423         if(!group){
25424             return;
25425         }
25426         
25427         for(var i in group){
25428             var fg = group[i].el.findParent('.form-group', false, true);
25429             if (Roo.bootstrap.version == 3) {
25430                 fg.removeClass([this.invalidClass, this.validClass]);
25431                 fg.addClass(this.validClass);
25432             } else {
25433                 fg.removeClass(['is-valid', 'is-invalid']);
25434                 fg.addClass('is-valid');
25435             }
25436         }
25437     },
25438     
25439      /**
25440      * Mark this field as invalid
25441      * @param {String} msg The validation message
25442      */
25443     markInvalid : function(msg)
25444     {
25445         if(this.allowBlank){
25446             return;
25447         }
25448         
25449         var _this = this;
25450         
25451         this.fireEvent('invalid', this, msg);
25452         
25453         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25454         
25455         if(this.groupId){
25456             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25457         }
25458         
25459         if(label){
25460             label.markInvalid();
25461         }
25462             
25463         if(this.inputType == 'radio'){
25464             
25465             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25466                 var fg = e.findParent('.form-group', false, true);
25467                 if (Roo.bootstrap.version == 3) {
25468                     fg.removeClass([_this.invalidClass, _this.validClass]);
25469                     fg.addClass(_this.invalidClass);
25470                 } else {
25471                     fg.removeClass(['is-invalid', 'is-valid']);
25472                     fg.addClass('is-invalid');
25473                 }
25474             });
25475             
25476             return;
25477         }
25478         
25479         if(!this.groupId){
25480             var fg = this.el.findParent('.form-group', false, true);
25481             if (Roo.bootstrap.version == 3) {
25482                 fg.removeClass([_this.invalidClass, _this.validClass]);
25483                 fg.addClass(_this.invalidClass);
25484             } else {
25485                 fg.removeClass(['is-invalid', 'is-valid']);
25486                 fg.addClass('is-invalid');
25487             }
25488             return;
25489         }
25490         
25491         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25492         
25493         if(!group){
25494             return;
25495         }
25496         
25497         for(var i in group){
25498             var fg = group[i].el.findParent('.form-group', false, true);
25499             if (Roo.bootstrap.version == 3) {
25500                 fg.removeClass([_this.invalidClass, _this.validClass]);
25501                 fg.addClass(_this.invalidClass);
25502             } else {
25503                 fg.removeClass(['is-invalid', 'is-valid']);
25504                 fg.addClass('is-invalid');
25505             }
25506         }
25507         
25508     },
25509     
25510     clearInvalid : function()
25511     {
25512         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25513         
25514         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25515         
25516         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25517         
25518         if (label && label.iconEl) {
25519             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25520             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25521         }
25522     },
25523     
25524     disable : function()
25525     {
25526         if(this.inputType != 'radio'){
25527             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25528             return;
25529         }
25530         
25531         var _this = this;
25532         
25533         if(this.rendered){
25534             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25535                 _this.getActionEl().addClass(this.disabledClass);
25536                 e.dom.disabled = true;
25537             });
25538         }
25539         
25540         this.disabled = true;
25541         this.fireEvent("disable", this);
25542         return this;
25543     },
25544
25545     enable : function()
25546     {
25547         if(this.inputType != 'radio'){
25548             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25549             return;
25550         }
25551         
25552         var _this = this;
25553         
25554         if(this.rendered){
25555             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25556                 _this.getActionEl().removeClass(this.disabledClass);
25557                 e.dom.disabled = false;
25558             });
25559         }
25560         
25561         this.disabled = false;
25562         this.fireEvent("enable", this);
25563         return this;
25564     },
25565     
25566     setBoxLabel : function(v)
25567     {
25568         this.boxLabel = v;
25569         
25570         if(this.rendered){
25571             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25572         }
25573     }
25574
25575 });
25576
25577 Roo.apply(Roo.bootstrap.form.CheckBox, {
25578     
25579     groups: {},
25580     
25581      /**
25582     * register a CheckBox Group
25583     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25584     */
25585     register : function(checkbox)
25586     {
25587         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25588             this.groups[checkbox.groupId] = {};
25589         }
25590         
25591         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25592             return;
25593         }
25594         
25595         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25596         
25597     },
25598     /**
25599     * fetch a CheckBox Group based on the group ID
25600     * @param {string} the group ID
25601     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25602     */
25603     get: function(groupId) {
25604         if (typeof(this.groups[groupId]) == 'undefined') {
25605             return false;
25606         }
25607         
25608         return this.groups[groupId] ;
25609     }
25610     
25611     
25612 });
25613 /*
25614  * - LGPL
25615  *
25616  * RadioItem
25617  * 
25618  */
25619
25620 /**
25621  * @class Roo.bootstrap.form.Radio
25622  * @extends Roo.bootstrap.Component
25623  * Bootstrap Radio class
25624  * @cfg {String} boxLabel - the label associated
25625  * @cfg {String} value - the value of radio
25626  * 
25627  * @constructor
25628  * Create a new Radio
25629  * @param {Object} config The config object
25630  */
25631 Roo.bootstrap.form.Radio = function(config){
25632     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25633     
25634 };
25635
25636 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25637     
25638     boxLabel : '',
25639     
25640     value : '',
25641     
25642     getAutoCreate : function()
25643     {
25644         var cfg = {
25645             tag : 'div',
25646             cls : 'form-group radio',
25647             cn : [
25648                 {
25649                     tag : 'label',
25650                     cls : 'box-label',
25651                     html : this.boxLabel
25652                 }
25653             ]
25654         };
25655         
25656         return cfg;
25657     },
25658     
25659     initEvents : function() 
25660     {
25661         this.parent().register(this);
25662         
25663         this.el.on('click', this.onClick, this);
25664         
25665     },
25666     
25667     onClick : function(e)
25668     {
25669         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25670             this.setChecked(true);
25671         }
25672     },
25673     
25674     setChecked : function(state, suppressEvent)
25675     {
25676         this.parent().setValue(this.value, suppressEvent);
25677         
25678     },
25679     
25680     setBoxLabel : function(v)
25681     {
25682         this.boxLabel = v;
25683         
25684         if(this.rendered){
25685             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25686         }
25687     }
25688     
25689 });
25690  
25691
25692  /*
25693  * - LGPL
25694  *
25695  * Input
25696  * 
25697  */
25698
25699 /**
25700  * @class Roo.bootstrap.form.SecurePass
25701  * @extends Roo.bootstrap.form.Input
25702  * Bootstrap SecurePass class
25703  *
25704  * 
25705  * @constructor
25706  * Create a new SecurePass
25707  * @param {Object} config The config object
25708  */
25709  
25710 Roo.bootstrap.form.SecurePass = function (config) {
25711     // these go here, so the translation tool can replace them..
25712     this.errors = {
25713         PwdEmpty: "Please type a password, and then retype it to confirm.",
25714         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25715         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25716         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25717         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25718         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25719         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25720         TooWeak: "Your password is Too Weak."
25721     },
25722     this.meterLabel = "Password strength:";
25723     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25724     this.meterClass = [
25725         "roo-password-meter-tooweak", 
25726         "roo-password-meter-weak", 
25727         "roo-password-meter-medium", 
25728         "roo-password-meter-strong", 
25729         "roo-password-meter-grey"
25730     ];
25731     
25732     this.errors = {};
25733     
25734     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25735 }
25736
25737 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25738     /**
25739      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25740      * {
25741      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25742      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25743      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25744      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25745      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25746      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25747      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25748      * })
25749      */
25750     // private
25751     
25752     meterWidth: 300,
25753     errorMsg :'',    
25754     errors: false,
25755     imageRoot: '/',
25756     /**
25757      * @cfg {String/Object} Label for the strength meter (defaults to
25758      * 'Password strength:')
25759      */
25760     // private
25761     meterLabel: '',
25762     /**
25763      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25764      * ['Weak', 'Medium', 'Strong'])
25765      */
25766     // private    
25767     pwdStrengths: false,    
25768     // private
25769     strength: 0,
25770     // private
25771     _lastPwd: null,
25772     // private
25773     kCapitalLetter: 0,
25774     kSmallLetter: 1,
25775     kDigit: 2,
25776     kPunctuation: 3,
25777     
25778     insecure: false,
25779     // private
25780     initEvents: function ()
25781     {
25782         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25783
25784         if (this.el.is('input[type=password]') && Roo.isSafari) {
25785             this.el.on('keydown', this.SafariOnKeyDown, this);
25786         }
25787
25788         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25789     },
25790     // private
25791     onRender: function (ct, position)
25792     {
25793         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25794         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25795         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25796
25797         this.trigger.createChild({
25798                    cn: [
25799                     {
25800                     //id: 'PwdMeter',
25801                     tag: 'div',
25802                     cls: 'roo-password-meter-grey col-xs-12',
25803                     style: {
25804                         //width: 0,
25805                         //width: this.meterWidth + 'px'                                                
25806                         }
25807                     },
25808                     {                            
25809                          cls: 'roo-password-meter-text'                          
25810                     }
25811                 ]            
25812         });
25813
25814          
25815         if (this.hideTrigger) {
25816             this.trigger.setDisplayed(false);
25817         }
25818         this.setSize(this.width || '', this.height || '');
25819     },
25820     // private
25821     onDestroy: function ()
25822     {
25823         if (this.trigger) {
25824             this.trigger.removeAllListeners();
25825             this.trigger.remove();
25826         }
25827         if (this.wrap) {
25828             this.wrap.remove();
25829         }
25830         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25831     },
25832     // private
25833     checkStrength: function ()
25834     {
25835         var pwd = this.inputEl().getValue();
25836         if (pwd == this._lastPwd) {
25837             return;
25838         }
25839
25840         var strength;
25841         if (this.ClientSideStrongPassword(pwd)) {
25842             strength = 3;
25843         } else if (this.ClientSideMediumPassword(pwd)) {
25844             strength = 2;
25845         } else if (this.ClientSideWeakPassword(pwd)) {
25846             strength = 1;
25847         } else {
25848             strength = 0;
25849         }
25850         
25851         Roo.log('strength1: ' + strength);
25852         
25853         //var pm = this.trigger.child('div/div/div').dom;
25854         var pm = this.trigger.child('div/div');
25855         pm.removeClass(this.meterClass);
25856         pm.addClass(this.meterClass[strength]);
25857                 
25858         
25859         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25860                 
25861         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25862         
25863         this._lastPwd = pwd;
25864     },
25865     reset: function ()
25866     {
25867         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25868         
25869         this._lastPwd = '';
25870         
25871         var pm = this.trigger.child('div/div');
25872         pm.removeClass(this.meterClass);
25873         pm.addClass('roo-password-meter-grey');        
25874         
25875         
25876         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25877         
25878         pt.innerHTML = '';
25879         this.inputEl().dom.type='password';
25880     },
25881     // private
25882     validateValue: function (value)
25883     {
25884         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25885             return false;
25886         }
25887         if (value.length == 0) {
25888             if (this.allowBlank) {
25889                 this.clearInvalid();
25890                 return true;
25891             }
25892
25893             this.markInvalid(this.errors.PwdEmpty);
25894             this.errorMsg = this.errors.PwdEmpty;
25895             return false;
25896         }
25897         
25898         if(this.insecure){
25899             return true;
25900         }
25901         
25902         if (!value.match(/[\x21-\x7e]+/)) {
25903             this.markInvalid(this.errors.PwdBadChar);
25904             this.errorMsg = this.errors.PwdBadChar;
25905             return false;
25906         }
25907         if (value.length < 6) {
25908             this.markInvalid(this.errors.PwdShort);
25909             this.errorMsg = this.errors.PwdShort;
25910             return false;
25911         }
25912         if (value.length > 16) {
25913             this.markInvalid(this.errors.PwdLong);
25914             this.errorMsg = this.errors.PwdLong;
25915             return false;
25916         }
25917         var strength;
25918         if (this.ClientSideStrongPassword(value)) {
25919             strength = 3;
25920         } else if (this.ClientSideMediumPassword(value)) {
25921             strength = 2;
25922         } else if (this.ClientSideWeakPassword(value)) {
25923             strength = 1;
25924         } else {
25925             strength = 0;
25926         }
25927
25928         
25929         if (strength < 2) {
25930             //this.markInvalid(this.errors.TooWeak);
25931             this.errorMsg = this.errors.TooWeak;
25932             //return false;
25933         }
25934         
25935         
25936         console.log('strength2: ' + strength);
25937         
25938         //var pm = this.trigger.child('div/div/div').dom;
25939         
25940         var pm = this.trigger.child('div/div');
25941         pm.removeClass(this.meterClass);
25942         pm.addClass(this.meterClass[strength]);
25943                 
25944         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25945                 
25946         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25947         
25948         this.errorMsg = ''; 
25949         return true;
25950     },
25951     // private
25952     CharacterSetChecks: function (type)
25953     {
25954         this.type = type;
25955         this.fResult = false;
25956     },
25957     // private
25958     isctype: function (character, type)
25959     {
25960         switch (type) {  
25961             case this.kCapitalLetter:
25962                 if (character >= 'A' && character <= 'Z') {
25963                     return true;
25964                 }
25965                 break;
25966             
25967             case this.kSmallLetter:
25968                 if (character >= 'a' && character <= 'z') {
25969                     return true;
25970                 }
25971                 break;
25972             
25973             case this.kDigit:
25974                 if (character >= '0' && character <= '9') {
25975                     return true;
25976                 }
25977                 break;
25978             
25979             case this.kPunctuation:
25980                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25981                     return true;
25982                 }
25983                 break;
25984             
25985             default:
25986                 return false;
25987         }
25988
25989     },
25990     // private
25991     IsLongEnough: function (pwd, size)
25992     {
25993         return !(pwd == null || isNaN(size) || pwd.length < size);
25994     },
25995     // private
25996     SpansEnoughCharacterSets: function (word, nb)
25997     {
25998         if (!this.IsLongEnough(word, nb))
25999         {
26000             return false;
26001         }
26002
26003         var characterSetChecks = new Array(
26004             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26005             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26006         );
26007         
26008         for (var index = 0; index < word.length; ++index) {
26009             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26010                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26011                     characterSetChecks[nCharSet].fResult = true;
26012                     break;
26013                 }
26014             }
26015         }
26016
26017         var nCharSets = 0;
26018         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26019             if (characterSetChecks[nCharSet].fResult) {
26020                 ++nCharSets;
26021             }
26022         }
26023
26024         if (nCharSets < nb) {
26025             return false;
26026         }
26027         return true;
26028     },
26029     // private
26030     ClientSideStrongPassword: function (pwd)
26031     {
26032         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26033     },
26034     // private
26035     ClientSideMediumPassword: function (pwd)
26036     {
26037         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26038     },
26039     // private
26040     ClientSideWeakPassword: function (pwd)
26041     {
26042         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26043     }
26044           
26045 });Roo.rtf = {}; // namespace
26046 Roo.rtf.Hex = function(hex)
26047 {
26048     this.hexstr = hex;
26049 };
26050 Roo.rtf.Paragraph = function(opts)
26051 {
26052     this.content = []; ///??? is that used?
26053 };Roo.rtf.Span = function(opts)
26054 {
26055     this.value = opts.value;
26056 };
26057
26058 Roo.rtf.Group = function(parent)
26059 {
26060     // we dont want to acutally store parent - it will make debug a nightmare..
26061     this.content = [];
26062     this.cn  = [];
26063      
26064        
26065     
26066 };
26067
26068 Roo.rtf.Group.prototype = {
26069     ignorable : false,
26070     content: false,
26071     cn: false,
26072     addContent : function(node) {
26073         // could set styles...
26074         this.content.push(node);
26075     },
26076     addChild : function(cn)
26077     {
26078         this.cn.push(cn);
26079     },
26080     // only for images really...
26081     toDataURL : function()
26082     {
26083         var mimetype = false;
26084         switch(true) {
26085             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26086                 mimetype = "image/png";
26087                 break;
26088              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26089                 mimetype = "image/jpeg";
26090                 break;
26091             default :
26092                 return 'about:blank'; // ?? error?
26093         }
26094         
26095         
26096         var hexstring = this.content[this.content.length-1].value;
26097         
26098         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26099             return String.fromCharCode(parseInt(a, 16));
26100         }).join(""));
26101     }
26102     
26103 };
26104 // this looks like it's normally the {rtf{ .... }}
26105 Roo.rtf.Document = function()
26106 {
26107     // we dont want to acutally store parent - it will make debug a nightmare..
26108     this.rtlch  = [];
26109     this.content = [];
26110     this.cn = [];
26111     
26112 };
26113 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26114     addChild : function(cn)
26115     {
26116         this.cn.push(cn);
26117         switch(cn.type) {
26118             case 'rtlch': // most content seems to be inside this??
26119             case 'listtext':
26120             case 'shpinst':
26121                 this.rtlch.push(cn);
26122                 return;
26123             default:
26124                 this[cn.type] = cn;
26125         }
26126         
26127     },
26128     
26129     getElementsByType : function(type)
26130     {
26131         var ret =  [];
26132         this._getElementsByType(type, ret, this.cn, 'rtf');
26133         return ret;
26134     },
26135     _getElementsByType : function (type, ret, search_array, path)
26136     {
26137         search_array.forEach(function(n,i) {
26138             if (n.type == type) {
26139                 n.path = path + '/' + n.type + ':' + i;
26140                 ret.push(n);
26141             }
26142             if (n.cn.length > 0) {
26143                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26144             }
26145         },this);
26146     }
26147     
26148 });
26149  
26150 Roo.rtf.Ctrl = function(opts)
26151 {
26152     this.value = opts.value;
26153     this.param = opts.param;
26154 };
26155 /**
26156  *
26157  *
26158  * based on this https://github.com/iarna/rtf-parser
26159  * it's really only designed to extract pict from pasted RTF 
26160  *
26161  * usage:
26162  *
26163  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26164  *  
26165  *
26166  */
26167
26168  
26169
26170
26171
26172 Roo.rtf.Parser = function(text) {
26173     //super({objectMode: true})
26174     this.text = '';
26175     this.parserState = this.parseText;
26176     
26177     // these are for interpeter...
26178     this.doc = {};
26179     ///this.parserState = this.parseTop
26180     this.groupStack = [];
26181     this.hexStore = [];
26182     this.doc = false;
26183     
26184     this.groups = []; // where we put the return.
26185     
26186     for (var ii = 0; ii < text.length; ++ii) {
26187         ++this.cpos;
26188         
26189         if (text[ii] === '\n') {
26190             ++this.row;
26191             this.col = 1;
26192         } else {
26193             ++this.col;
26194         }
26195         this.parserState(text[ii]);
26196     }
26197     
26198     
26199     
26200 };
26201 Roo.rtf.Parser.prototype = {
26202     text : '', // string being parsed..
26203     controlWord : '',
26204     controlWordParam :  '',
26205     hexChar : '',
26206     doc : false,
26207     group: false,
26208     groupStack : false,
26209     hexStore : false,
26210     
26211     
26212     cpos : 0, 
26213     row : 1, // reportin?
26214     col : 1, //
26215
26216      
26217     push : function (el)
26218     {
26219         var m = 'cmd'+ el.type;
26220         if (typeof(this[m]) == 'undefined') {
26221             Roo.log('invalid cmd:' + el.type);
26222             return;
26223         }
26224         this[m](el);
26225         //Roo.log(el);
26226     },
26227     flushHexStore : function()
26228     {
26229         if (this.hexStore.length < 1) {
26230             return;
26231         }
26232         var hexstr = this.hexStore.map(
26233             function(cmd) {
26234                 return cmd.value;
26235         }).join('');
26236         
26237         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26238               
26239             
26240         this.hexStore.splice(0)
26241         
26242     },
26243     
26244     cmdgroupstart : function()
26245     {
26246         this.flushHexStore();
26247         if (this.group) {
26248             this.groupStack.push(this.group);
26249         }
26250          // parent..
26251         if (this.doc === false) {
26252             this.group = this.doc = new Roo.rtf.Document();
26253             return;
26254             
26255         }
26256         this.group = new Roo.rtf.Group(this.group);
26257     },
26258     cmdignorable : function()
26259     {
26260         this.flushHexStore();
26261         this.group.ignorable = true;
26262     },
26263     cmdendparagraph : function()
26264     {
26265         this.flushHexStore();
26266         this.group.addContent(new Roo.rtf.Paragraph());
26267     },
26268     cmdgroupend : function ()
26269     {
26270         this.flushHexStore();
26271         var endingGroup = this.group;
26272         
26273         
26274         this.group = this.groupStack.pop();
26275         if (this.group) {
26276             this.group.addChild(endingGroup);
26277         }
26278         
26279         
26280         
26281         var doc = this.group || this.doc;
26282         //if (endingGroup instanceof FontTable) {
26283         //  doc.fonts = endingGroup.table
26284         //} else if (endingGroup instanceof ColorTable) {
26285         //  doc.colors = endingGroup.table
26286         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26287         if (endingGroup.ignorable === false) {
26288             //code
26289             this.groups.push(endingGroup);
26290            // Roo.log( endingGroup );
26291         }
26292             //Roo.each(endingGroup.content, function(item)) {
26293             //    doc.addContent(item);
26294             //}
26295             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26296         //}
26297     },
26298     cmdtext : function (cmd)
26299     {
26300         this.flushHexStore();
26301         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26302             //this.group = this.doc
26303             return;  // we really don't care about stray text...
26304         }
26305         this.group.addContent(new Roo.rtf.Span(cmd));
26306     },
26307     cmdcontrolword : function (cmd)
26308     {
26309         this.flushHexStore();
26310         if (!this.group.type) {
26311             this.group.type = cmd.value;
26312             return;
26313         }
26314         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26315         // we actually don't care about ctrl words...
26316         return ;
26317         /*
26318         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26319         if (this[method]) {
26320             this[method](cmd.param)
26321         } else {
26322             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26323         }
26324         */
26325     },
26326     cmdhexchar : function(cmd) {
26327         this.hexStore.push(cmd);
26328     },
26329     cmderror : function(cmd) {
26330         throw cmd.value;
26331     },
26332     
26333     /*
26334       _flush (done) {
26335         if (this.text !== '\u0000') this.emitText()
26336         done()
26337       }
26338       */
26339       
26340       
26341     parseText : function(c)
26342     {
26343         if (c === '\\') {
26344             this.parserState = this.parseEscapes;
26345         } else if (c === '{') {
26346             this.emitStartGroup();
26347         } else if (c === '}') {
26348             this.emitEndGroup();
26349         } else if (c === '\x0A' || c === '\x0D') {
26350             // cr/lf are noise chars
26351         } else {
26352             this.text += c;
26353         }
26354     },
26355     
26356     parseEscapes: function (c)
26357     {
26358         if (c === '\\' || c === '{' || c === '}') {
26359             this.text += c;
26360             this.parserState = this.parseText;
26361         } else {
26362             this.parserState = this.parseControlSymbol;
26363             this.parseControlSymbol(c);
26364         }
26365     },
26366     parseControlSymbol: function(c)
26367     {
26368         if (c === '~') {
26369             this.text += '\u00a0'; // nbsp
26370             this.parserState = this.parseText
26371         } else if (c === '-') {
26372              this.text += '\u00ad'; // soft hyphen
26373         } else if (c === '_') {
26374             this.text += '\u2011'; // non-breaking hyphen
26375         } else if (c === '*') {
26376             this.emitIgnorable();
26377             this.parserState = this.parseText;
26378         } else if (c === "'") {
26379             this.parserState = this.parseHexChar;
26380         } else if (c === '|') { // formula cacter
26381             this.emitFormula();
26382             this.parserState = this.parseText;
26383         } else if (c === ':') { // subentry in an index entry
26384             this.emitIndexSubEntry();
26385             this.parserState = this.parseText;
26386         } else if (c === '\x0a') {
26387             this.emitEndParagraph();
26388             this.parserState = this.parseText;
26389         } else if (c === '\x0d') {
26390             this.emitEndParagraph();
26391             this.parserState = this.parseText;
26392         } else {
26393             this.parserState = this.parseControlWord;
26394             this.parseControlWord(c);
26395         }
26396     },
26397     parseHexChar: function (c)
26398     {
26399         if (/^[A-Fa-f0-9]$/.test(c)) {
26400             this.hexChar += c;
26401             if (this.hexChar.length >= 2) {
26402               this.emitHexChar();
26403               this.parserState = this.parseText;
26404             }
26405             return;
26406         }
26407         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26408         this.parserState = this.parseText;
26409         
26410     },
26411     parseControlWord : function(c)
26412     {
26413         if (c === ' ') {
26414             this.emitControlWord();
26415             this.parserState = this.parseText;
26416         } else if (/^[-\d]$/.test(c)) {
26417             this.parserState = this.parseControlWordParam;
26418             this.controlWordParam += c;
26419         } else if (/^[A-Za-z]$/.test(c)) {
26420           this.controlWord += c;
26421         } else {
26422           this.emitControlWord();
26423           this.parserState = this.parseText;
26424           this.parseText(c);
26425         }
26426     },
26427     parseControlWordParam : function (c) {
26428         if (/^\d$/.test(c)) {
26429           this.controlWordParam += c;
26430         } else if (c === ' ') {
26431           this.emitControlWord();
26432           this.parserState = this.parseText;
26433         } else {
26434           this.emitControlWord();
26435           this.parserState = this.parseText;
26436           this.parseText(c);
26437         }
26438     },
26439     
26440     
26441     
26442     
26443     emitText : function () {
26444         if (this.text === '') {
26445             return;
26446         }
26447         this.push({
26448             type: 'text',
26449             value: this.text,
26450             pos: this.cpos,
26451             row: this.row,
26452             col: this.col
26453         });
26454         this.text = ''
26455     },
26456     emitControlWord : function ()
26457     {
26458         this.emitText();
26459         if (this.controlWord === '') {
26460             // do we want to track this - it seems just to cause problems.
26461             //this.emitError('empty control word');
26462         } else {
26463             this.push({
26464                   type: 'controlword',
26465                   value: this.controlWord,
26466                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26467                   pos: this.cpos,
26468                   row: this.row,
26469                   col: this.col
26470             });
26471         }
26472         this.controlWord = '';
26473         this.controlWordParam = '';
26474     },
26475     emitStartGroup : function ()
26476     {
26477         this.emitText();
26478         this.push({
26479             type: 'groupstart',
26480             pos: this.cpos,
26481             row: this.row,
26482             col: this.col
26483         });
26484     },
26485     emitEndGroup : function ()
26486     {
26487         this.emitText();
26488         this.push({
26489             type: 'groupend',
26490             pos: this.cpos,
26491             row: this.row,
26492             col: this.col
26493         });
26494     },
26495     emitIgnorable : function ()
26496     {
26497         this.emitText();
26498         this.push({
26499             type: 'ignorable',
26500             pos: this.cpos,
26501             row: this.row,
26502             col: this.col
26503         });
26504     },
26505     emitHexChar : function ()
26506     {
26507         this.emitText();
26508         this.push({
26509             type: 'hexchar',
26510             value: this.hexChar,
26511             pos: this.cpos,
26512             row: this.row,
26513             col: this.col
26514         });
26515         this.hexChar = ''
26516     },
26517     emitError : function (message)
26518     {
26519       this.emitText();
26520       this.push({
26521             type: 'error',
26522             value: message,
26523             row: this.row,
26524             col: this.col,
26525             char: this.cpos //,
26526             //stack: new Error().stack
26527         });
26528     },
26529     emitEndParagraph : function () {
26530         this.emitText();
26531         this.push({
26532             type: 'endparagraph',
26533             pos: this.cpos,
26534             row: this.row,
26535             col: this.col
26536         });
26537     }
26538      
26539 } ;
26540 Roo.htmleditor = {};
26541  
26542 /**
26543  * @class Roo.htmleditor.Filter
26544  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26545  * @cfg {DomElement} node The node to iterate and filter
26546  * @cfg {boolean|String|Array} tag Tags to replace 
26547  * @constructor
26548  * Create a new Filter.
26549  * @param {Object} config Configuration options
26550  */
26551
26552
26553
26554 Roo.htmleditor.Filter = function(cfg) {
26555     Roo.apply(this.cfg);
26556     // this does not actually call walk as it's really just a abstract class
26557 }
26558
26559
26560 Roo.htmleditor.Filter.prototype = {
26561     
26562     node: false,
26563     
26564     tag: false,
26565
26566     // overrride to do replace comments.
26567     replaceComment : false,
26568     
26569     // overrride to do replace or do stuff with tags..
26570     replaceTag : false,
26571     
26572     walk : function(dom)
26573     {
26574         Roo.each( Array.from(dom.childNodes), function( e ) {
26575             switch(true) {
26576                 
26577                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26578                     this.replaceComment(e);
26579                     return;
26580                 
26581                 case e.nodeType != 1: //not a node.
26582                     return;
26583                 
26584                 case this.tag === true: // everything
26585                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26586                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26587                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26588                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26589                     if (this.replaceTag && false === this.replaceTag(e)) {
26590                         return;
26591                     }
26592                     if (e.hasChildNodes()) {
26593                         this.walk(e);
26594                     }
26595                     return;
26596                 
26597                 default:    // tags .. that do not match.
26598                     if (e.hasChildNodes()) {
26599                         this.walk(e);
26600                     }
26601             }
26602             
26603         }, this);
26604         
26605     },
26606     
26607     
26608     removeNodeKeepChildren : function( node)
26609     {
26610     
26611         ar = Array.from(node.childNodes);
26612         for (var i = 0; i < ar.length; i++) {
26613          
26614             node.removeChild(ar[i]);
26615             // what if we need to walk these???
26616             node.parentNode.insertBefore(ar[i], node);
26617            
26618         }
26619         node.parentNode.removeChild(node);
26620     }
26621 }; 
26622
26623 /**
26624  * @class Roo.htmleditor.FilterAttributes
26625  * clean attributes and  styles including http:// etc.. in attribute
26626  * @constructor
26627 * Run a new Attribute Filter
26628 * @param {Object} config Configuration options
26629  */
26630 Roo.htmleditor.FilterAttributes = function(cfg)
26631 {
26632     Roo.apply(this, cfg);
26633     this.attrib_black = this.attrib_black || [];
26634     this.attrib_white = this.attrib_white || [];
26635
26636     this.attrib_clean = this.attrib_clean || [];
26637     this.style_white = this.style_white || [];
26638     this.style_black = this.style_black || [];
26639     this.walk(cfg.node);
26640 }
26641
26642 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26643 {
26644     tag: true, // all tags
26645     
26646     attrib_black : false, // array
26647     attrib_clean : false,
26648     attrib_white : false,
26649
26650     style_white : false,
26651     style_black : false,
26652      
26653      
26654     replaceTag : function(node)
26655     {
26656         if (!node.attributes || !node.attributes.length) {
26657             return true;
26658         }
26659         
26660         for (var i = node.attributes.length-1; i > -1 ; i--) {
26661             var a = node.attributes[i];
26662             //console.log(a);
26663             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26664                 node.removeAttribute(a.name);
26665                 continue;
26666             }
26667             
26668             
26669             
26670             if (a.name.toLowerCase().substr(0,2)=='on')  {
26671                 node.removeAttribute(a.name);
26672                 continue;
26673             }
26674             
26675             
26676             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26677                 node.removeAttribute(a.name);
26678                 continue;
26679             }
26680             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26681                 this.cleanAttr(node,a.name,a.value); // fixme..
26682                 continue;
26683             }
26684             if (a.name == 'style') {
26685                 this.cleanStyle(node,a.name,a.value);
26686                 continue;
26687             }
26688             /// clean up MS crap..
26689             // tecnically this should be a list of valid class'es..
26690             
26691             
26692             if (a.name == 'class') {
26693                 if (a.value.match(/^Mso/)) {
26694                     node.removeAttribute('class');
26695                 }
26696                 
26697                 if (a.value.match(/^body$/)) {
26698                     node.removeAttribute('class');
26699                 }
26700                 continue;
26701             }
26702             
26703             
26704             // style cleanup!?
26705             // class cleanup?
26706             
26707         }
26708         return true; // clean children
26709     },
26710         
26711     cleanAttr: function(node, n,v)
26712     {
26713         
26714         if (v.match(/^\./) || v.match(/^\//)) {
26715             return;
26716         }
26717         if (v.match(/^(http|https):\/\//)
26718             || v.match(/^mailto:/) 
26719             || v.match(/^ftp:/)
26720             || v.match(/^data:/)
26721             ) {
26722             return;
26723         }
26724         if (v.match(/^#/)) {
26725             return;
26726         }
26727         if (v.match(/^\{/)) { // allow template editing.
26728             return;
26729         }
26730 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26731         node.removeAttribute(n);
26732         
26733     },
26734     cleanStyle : function(node,  n,v)
26735     {
26736         if (v.match(/expression/)) { //XSS?? should we even bother..
26737             node.removeAttribute(n);
26738             return;
26739         }
26740         
26741         var parts = v.split(/;/);
26742         var clean = [];
26743         
26744         Roo.each(parts, function(p) {
26745             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26746             if (!p.length) {
26747                 return true;
26748             }
26749             var l = p.split(':').shift().replace(/\s+/g,'');
26750             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26751             
26752             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26753                 return true;
26754             }
26755             //Roo.log()
26756             // only allow 'c whitelisted system attributes'
26757             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26758                 return true;
26759             }
26760             
26761             
26762             clean.push(p);
26763             return true;
26764         },this);
26765         if (clean.length) { 
26766             node.setAttribute(n, clean.join(';'));
26767         } else {
26768             node.removeAttribute(n);
26769         }
26770         
26771     }
26772         
26773         
26774         
26775     
26776 });/**
26777  * @class Roo.htmleditor.FilterBlack
26778  * remove blacklisted elements.
26779  * @constructor
26780  * Run a new Blacklisted Filter
26781  * @param {Object} config Configuration options
26782  */
26783
26784 Roo.htmleditor.FilterBlack = function(cfg)
26785 {
26786     Roo.apply(this, cfg);
26787     this.walk(cfg.node);
26788 }
26789
26790 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26791 {
26792     tag : true, // all elements.
26793    
26794     replaceTag : function(n)
26795     {
26796         n.parentNode.removeChild(n);
26797     }
26798 });
26799 /**
26800  * @class Roo.htmleditor.FilterComment
26801  * remove comments.
26802  * @constructor
26803 * Run a new Comments Filter
26804 * @param {Object} config Configuration options
26805  */
26806 Roo.htmleditor.FilterComment = function(cfg)
26807 {
26808     this.walk(cfg.node);
26809 }
26810
26811 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26812 {
26813   
26814     replaceComment : function(n)
26815     {
26816         n.parentNode.removeChild(n);
26817     }
26818 });/**
26819  * @class Roo.htmleditor.FilterKeepChildren
26820  * remove tags but keep children
26821  * @constructor
26822  * Run a new Keep Children Filter
26823  * @param {Object} config Configuration options
26824  */
26825
26826 Roo.htmleditor.FilterKeepChildren = function(cfg)
26827 {
26828     Roo.apply(this, cfg);
26829     if (this.tag === false) {
26830         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26831     }
26832     // hacky?
26833     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26834         this.cleanNamespace = true;
26835     }
26836         
26837     this.walk(cfg.node);
26838 }
26839
26840 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26841 {
26842     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26843   
26844     replaceTag : function(node)
26845     {
26846         // walk children...
26847         //Roo.log(node.tagName);
26848         var ar = Array.from(node.childNodes);
26849         //remove first..
26850         
26851         for (var i = 0; i < ar.length; i++) {
26852             var e = ar[i];
26853             if (e.nodeType == 1) {
26854                 if (
26855                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26856                     || // array and it matches
26857                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26858                     ||
26859                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26860                     ||
26861                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26862                 ) {
26863                     this.replaceTag(ar[i]); // child is blacklisted as well...
26864                     continue;
26865                 }
26866             }
26867         }  
26868         ar = Array.from(node.childNodes);
26869         for (var i = 0; i < ar.length; i++) {
26870          
26871             node.removeChild(ar[i]);
26872             // what if we need to walk these???
26873             node.parentNode.insertBefore(ar[i], node);
26874             if (this.tag !== false) {
26875                 this.walk(ar[i]);
26876                 
26877             }
26878         }
26879         //Roo.log("REMOVE:" + node.tagName);
26880         node.parentNode.removeChild(node);
26881         return false; // don't walk children
26882         
26883         
26884     }
26885 });/**
26886  * @class Roo.htmleditor.FilterParagraph
26887  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26888  * like on 'push' to remove the <p> tags and replace them with line breaks.
26889  * @constructor
26890  * Run a new Paragraph Filter
26891  * @param {Object} config Configuration options
26892  */
26893
26894 Roo.htmleditor.FilterParagraph = function(cfg)
26895 {
26896     // no need to apply config.
26897     this.walk(cfg.node);
26898 }
26899
26900 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26901 {
26902     
26903      
26904     tag : 'P',
26905     
26906      
26907     replaceTag : function(node)
26908     {
26909         
26910         if (node.childNodes.length == 1 &&
26911             node.childNodes[0].nodeType == 3 &&
26912             node.childNodes[0].textContent.trim().length < 1
26913             ) {
26914             // remove and replace with '<BR>';
26915             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26916             return false; // no need to walk..
26917         }
26918         var ar = Array.from(node.childNodes);
26919         for (var i = 0; i < ar.length; i++) {
26920             node.removeChild(ar[i]);
26921             // what if we need to walk these???
26922             node.parentNode.insertBefore(ar[i], node);
26923         }
26924         // now what about this?
26925         // <p> &nbsp; </p>
26926         
26927         // double BR.
26928         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26929         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26930         node.parentNode.removeChild(node);
26931         
26932         return false;
26933
26934     }
26935     
26936 });/**
26937  * @class Roo.htmleditor.FilterSpan
26938  * filter span's with no attributes out..
26939  * @constructor
26940  * Run a new Span Filter
26941  * @param {Object} config Configuration options
26942  */
26943
26944 Roo.htmleditor.FilterSpan = function(cfg)
26945 {
26946     // no need to apply config.
26947     this.walk(cfg.node);
26948 }
26949
26950 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26951 {
26952      
26953     tag : 'SPAN',
26954      
26955  
26956     replaceTag : function(node)
26957     {
26958         if (node.attributes && node.attributes.length > 0) {
26959             return true; // walk if there are any.
26960         }
26961         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26962         return false;
26963      
26964     }
26965     
26966 });/**
26967  * @class Roo.htmleditor.FilterTableWidth
26968   try and remove table width data - as that frequently messes up other stuff.
26969  * 
26970  *      was cleanTableWidths.
26971  *
26972  * Quite often pasting from word etc.. results in tables with column and widths.
26973  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26974  *
26975  * @constructor
26976  * Run a new Table Filter
26977  * @param {Object} config Configuration options
26978  */
26979
26980 Roo.htmleditor.FilterTableWidth = function(cfg)
26981 {
26982     // no need to apply config.
26983     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26984     this.walk(cfg.node);
26985 }
26986
26987 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26988 {
26989      
26990      
26991     
26992     replaceTag: function(node) {
26993         
26994         
26995       
26996         if (node.hasAttribute('width')) {
26997             node.removeAttribute('width');
26998         }
26999         
27000          
27001         if (node.hasAttribute("style")) {
27002             // pretty basic...
27003             
27004             var styles = node.getAttribute("style").split(";");
27005             var nstyle = [];
27006             Roo.each(styles, function(s) {
27007                 if (!s.match(/:/)) {
27008                     return;
27009                 }
27010                 var kv = s.split(":");
27011                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27012                     return;
27013                 }
27014                 // what ever is left... we allow.
27015                 nstyle.push(s);
27016             });
27017             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27018             if (!nstyle.length) {
27019                 node.removeAttribute('style');
27020             }
27021         }
27022         
27023         return true; // continue doing children..
27024     }
27025 });/**
27026  * @class Roo.htmleditor.FilterWord
27027  * try and clean up all the mess that Word generates.
27028  * 
27029  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
27030  
27031  * @constructor
27032  * Run a new Span Filter
27033  * @param {Object} config Configuration options
27034  */
27035
27036 Roo.htmleditor.FilterWord = function(cfg)
27037 {
27038     // no need to apply config.
27039     this.replaceDocBullets(cfg.node);
27040     
27041     this.replaceAname(cfg.node);
27042     // this is disabled as the removal is done by other filters;
27043    // this.walk(cfg.node);
27044     
27045     
27046 }
27047
27048 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27049 {
27050     tag: true,
27051      
27052     
27053     /**
27054      * Clean up MS wordisms...
27055      */
27056     replaceTag : function(node)
27057     {
27058          
27059         // no idea what this does - span with text, replaceds with just text.
27060         if(
27061                 node.nodeName == 'SPAN' &&
27062                 !node.hasAttributes() &&
27063                 node.childNodes.length == 1 &&
27064                 node.firstChild.nodeName == "#text"  
27065         ) {
27066             var textNode = node.firstChild;
27067             node.removeChild(textNode);
27068             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27069                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27070             }
27071             node.parentNode.insertBefore(textNode, node);
27072             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27073                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27074             }
27075             
27076             node.parentNode.removeChild(node);
27077             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27078         }
27079         
27080    
27081         
27082         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27083             node.parentNode.removeChild(node);
27084             return false; // dont do chidlren
27085         }
27086         //Roo.log(node.tagName);
27087         // remove - but keep children..
27088         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27089             //Roo.log('-- removed');
27090             while (node.childNodes.length) {
27091                 var cn = node.childNodes[0];
27092                 node.removeChild(cn);
27093                 node.parentNode.insertBefore(cn, node);
27094                 // move node to parent - and clean it..
27095                 if (cn.nodeType == 1) {
27096                     this.replaceTag(cn);
27097                 }
27098                 
27099             }
27100             node.parentNode.removeChild(node);
27101             /// no need to iterate chidlren = it's got none..
27102             //this.iterateChildren(node, this.cleanWord);
27103             return false; // no need to iterate children.
27104         }
27105         // clean styles
27106         if (node.className.length) {
27107             
27108             var cn = node.className.split(/\W+/);
27109             var cna = [];
27110             Roo.each(cn, function(cls) {
27111                 if (cls.match(/Mso[a-zA-Z]+/)) {
27112                     return;
27113                 }
27114                 cna.push(cls);
27115             });
27116             node.className = cna.length ? cna.join(' ') : '';
27117             if (!cna.length) {
27118                 node.removeAttribute("class");
27119             }
27120         }
27121         
27122         if (node.hasAttribute("lang")) {
27123             node.removeAttribute("lang");
27124         }
27125         
27126         if (node.hasAttribute("style")) {
27127             
27128             var styles = node.getAttribute("style").split(";");
27129             var nstyle = [];
27130             Roo.each(styles, function(s) {
27131                 if (!s.match(/:/)) {
27132                     return;
27133                 }
27134                 var kv = s.split(":");
27135                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27136                     return;
27137                 }
27138                 // what ever is left... we allow.
27139                 nstyle.push(s);
27140             });
27141             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27142             if (!nstyle.length) {
27143                 node.removeAttribute('style');
27144             }
27145         }
27146         return true; // do children
27147         
27148         
27149         
27150     },
27151     
27152     styleToObject: function(node)
27153     {
27154         var styles = (node.getAttribute("style") || '').split(";");
27155         var ret = {};
27156         Roo.each(styles, function(s) {
27157             if (!s.match(/:/)) {
27158                 return;
27159             }
27160             var kv = s.split(":");
27161              
27162             // what ever is left... we allow.
27163             ret[kv[0].trim()] = kv[1];
27164         });
27165         return ret;
27166     },
27167     
27168     
27169     replaceAname : function (doc)
27170     {
27171         // replace all the a/name without..
27172         var aa = Array.from(doc.getElementsByTagName('a'));
27173         for (var i = 0; i  < aa.length; i++) {
27174             var a = aa[i];
27175             if (a.hasAttribute("name")) {
27176                 a.removeAttribute("name");
27177             }
27178             if (a.hasAttribute("href")) {
27179                 continue;
27180             }
27181             // reparent children.
27182             this.removeNodeKeepChildren(a);
27183             
27184         }
27185         
27186         
27187         
27188     },
27189
27190     
27191     
27192     replaceDocBullets : function(doc)
27193     {
27194         // this is a bit odd - but it appears some indents use ql-indent-1
27195          //Roo.log(doc.innerHTML);
27196         
27197         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27198         for( var i = 0; i < listpara.length; i ++) {
27199             listpara[i].className = "MsoListParagraph";
27200         }
27201         
27202         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27203         for( var i = 0; i < listpara.length; i ++) {
27204             listpara[i].className = "MsoListParagraph";
27205         }
27206         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27207         for( var i = 0; i < listpara.length; i ++) {
27208             listpara[i].className = "MsoListParagraph";
27209         }
27210         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27211         for( var i = 0; i < listpara.length; i ++) {
27212             listpara[i].className = "MsoListParagraph";
27213         }
27214         
27215         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27216         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27217         for( var i = 0; i < htwo.length; i ++) {
27218             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27219                 htwo[i].className = "MsoListParagraph";
27220             }
27221         }
27222         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27223         for( var i = 0; i < listpara.length; i ++) {
27224             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27225                 listpara[i].className = "MsoListParagraph";
27226             } else {
27227                 listpara[i].className = "MsoNormalx";
27228             }
27229         }
27230        
27231         listpara = doc.getElementsByClassName('MsoListParagraph');
27232         // Roo.log(doc.innerHTML);
27233         
27234         
27235         
27236         while(listpara.length) {
27237             
27238             this.replaceDocBullet(listpara.item(0));
27239         }
27240       
27241     },
27242     
27243      
27244     
27245     replaceDocBullet : function(p)
27246     {
27247         // gather all the siblings.
27248         var ns = p,
27249             parent = p.parentNode,
27250             doc = parent.ownerDocument,
27251             items = [];
27252             
27253         var listtype = 'ul';   
27254         while (ns) {
27255             if (ns.nodeType != 1) {
27256                 ns = ns.nextSibling;
27257                 continue;
27258             }
27259             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27260                 break;
27261             }
27262             var spans = ns.getElementsByTagName('span');
27263             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27264                 items.push(ns);
27265                 ns = ns.nextSibling;
27266                 has_list = true;
27267                 if (spans.length && spans[0].hasAttribute('style')) {
27268                     var  style = this.styleToObject(spans[0]);
27269                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
27270                         listtype = 'ol';
27271                     }
27272                 }
27273                 
27274                 continue;
27275             }
27276             var spans = ns.getElementsByTagName('span');
27277             if (!spans.length) {
27278                 break;
27279             }
27280             var has_list  = false;
27281             for(var i = 0; i < spans.length; i++) {
27282                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27283                     has_list = true;
27284                     break;
27285                 }
27286             }
27287             if (!has_list) {
27288                 break;
27289             }
27290             items.push(ns);
27291             ns = ns.nextSibling;
27292             
27293             
27294         }
27295         if (!items.length) {
27296             ns.className = "";
27297             return;
27298         }
27299         
27300         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27301         parent.insertBefore(ul, p);
27302         var lvl = 0;
27303         var stack = [ ul ];
27304         var last_li = false;
27305         
27306         var margin_to_depth = {};
27307         max_margins = -1;
27308         
27309         items.forEach(function(n, ipos) {
27310             //Roo.log("got innertHMLT=" + n.innerHTML);
27311             
27312             var spans = n.getElementsByTagName('span');
27313             if (!spans.length) {
27314                 //Roo.log("No spans found");
27315                  
27316                 parent.removeChild(n);
27317                 
27318                 
27319                 return; // skip it...
27320             }
27321            
27322                 
27323             var num = 1;
27324             var style = {};
27325             for(var i = 0; i < spans.length; i++) {
27326             
27327                 style = this.styleToObject(spans[i]);
27328                 if (typeof(style['mso-list']) == 'undefined') {
27329                     continue;
27330                 }
27331                 if (listtype == 'ol') {
27332                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27333                 }
27334                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27335                 break;
27336             }
27337             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27338             style = this.styleToObject(n); // mo-list is from the parent node.
27339             if (typeof(style['mso-list']) == 'undefined') {
27340                 //Roo.log("parent is missing level");
27341                   
27342                 parent.removeChild(n);
27343                  
27344                 return;
27345             }
27346             
27347             var margin = style['margin-left'];
27348             if (typeof(margin_to_depth[margin]) == 'undefined') {
27349                 max_margins++;
27350                 margin_to_depth[margin] = max_margins;
27351             }
27352             nlvl = margin_to_depth[margin] ;
27353              
27354             if (nlvl > lvl) {
27355                 //new indent
27356                 var nul = doc.createElement(listtype); // what about number lists...
27357                 if (!last_li) {
27358                     last_li = doc.createElement('li');
27359                     stack[lvl].appendChild(last_li);
27360                 }
27361                 last_li.appendChild(nul);
27362                 stack[nlvl] = nul;
27363                 
27364             }
27365             lvl = nlvl;
27366             
27367             // not starting at 1..
27368             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27369                 stack[nlvl].setAttribute("start", num);
27370             }
27371             
27372             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27373             last_li = nli;
27374             nli.innerHTML = n.innerHTML;
27375             //Roo.log("innerHTML = " + n.innerHTML);
27376             parent.removeChild(n);
27377             
27378              
27379              
27380             
27381         },this);
27382         
27383         
27384         
27385         
27386     }
27387     
27388     
27389     
27390 });
27391 /**
27392  * @class Roo.htmleditor.FilterStyleToTag
27393  * part of the word stuff... - certain 'styles' should be converted to tags.
27394  * eg.
27395  *   font-weight: bold -> bold
27396  *   ?? super / subscrit etc..
27397  * 
27398  * @constructor
27399 * Run a new style to tag filter.
27400 * @param {Object} config Configuration options
27401  */
27402 Roo.htmleditor.FilterStyleToTag = function(cfg)
27403 {
27404     
27405     this.tags = {
27406         B  : [ 'fontWeight' , 'bold'],
27407         I :  [ 'fontStyle' , 'italic'],
27408         //pre :  [ 'font-style' , 'italic'],
27409         // h1.. h6 ?? font-size?
27410         SUP : [ 'verticalAlign' , 'super' ],
27411         SUB : [ 'verticalAlign' , 'sub' ]
27412         
27413         
27414     };
27415     
27416     Roo.apply(this, cfg);
27417      
27418     
27419     this.walk(cfg.node);
27420     
27421     
27422     
27423 }
27424
27425
27426 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27427 {
27428     tag: true, // all tags
27429     
27430     tags : false,
27431     
27432     
27433     replaceTag : function(node)
27434     {
27435         
27436         
27437         if (node.getAttribute("style") === null) {
27438             return true;
27439         }
27440         var inject = [];
27441         for (var k in this.tags) {
27442             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27443                 inject.push(k);
27444                 node.style.removeProperty(this.tags[k][0]);
27445             }
27446         }
27447         if (!inject.length) {
27448             return true; 
27449         }
27450         var cn = Array.from(node.childNodes);
27451         var nn = node;
27452         Roo.each(inject, function(t) {
27453             var nc = node.ownerDocument.createElement(t);
27454             nn.appendChild(nc);
27455             nn = nc;
27456         });
27457         for(var i = 0;i < cn.length;cn++) {
27458             node.removeChild(cn[i]);
27459             nn.appendChild(cn[i]);
27460         }
27461         return true /// iterate thru
27462     }
27463     
27464 })/**
27465  * @class Roo.htmleditor.FilterLongBr
27466  * BR/BR/BR - keep a maximum of 2...
27467  * @constructor
27468  * Run a new Long BR Filter
27469  * @param {Object} config Configuration options
27470  */
27471
27472 Roo.htmleditor.FilterLongBr = function(cfg)
27473 {
27474     // no need to apply config.
27475     this.walk(cfg.node);
27476 }
27477
27478 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27479 {
27480     
27481      
27482     tag : 'BR',
27483     
27484      
27485     replaceTag : function(node)
27486     {
27487         
27488         var ps = node.nextSibling;
27489         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27490             ps = ps.nextSibling;
27491         }
27492         
27493         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27494             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27495             return false;
27496         }
27497         
27498         if (!ps || ps.nodeType != 1) {
27499             return false;
27500         }
27501         
27502         if (!ps || ps.tagName != 'BR') {
27503            
27504             return false;
27505         }
27506         
27507         
27508         
27509         
27510         
27511         if (!node.previousSibling) {
27512             return false;
27513         }
27514         var ps = node.previousSibling;
27515         
27516         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27517             ps = ps.previousSibling;
27518         }
27519         if (!ps || ps.nodeType != 1) {
27520             return false;
27521         }
27522         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27523         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27524             return false;
27525         }
27526         
27527         node.parentNode.removeChild(node); // remove me...
27528         
27529         return false; // no need to do children
27530
27531     }
27532     
27533 }); 
27534
27535 /**
27536  * @class Roo.htmleditor.FilterBlock
27537  * removes id / data-block and contenteditable that are associated with blocks
27538  * usage should be done on a cloned copy of the dom
27539  * @constructor
27540 * Run a new Attribute Filter { node : xxxx }}
27541 * @param {Object} config Configuration options
27542  */
27543 Roo.htmleditor.FilterBlock = function(cfg)
27544 {
27545     Roo.apply(this, cfg);
27546     var qa = cfg.node.querySelectorAll;
27547     this.removeAttributes('data-block');
27548     this.removeAttributes('contenteditable');
27549     this.removeAttributes('id');
27550     
27551 }
27552
27553 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27554 {
27555     node: true, // all tags
27556      
27557      
27558     removeAttributes : function(attr)
27559     {
27560         var ar = this.node.querySelectorAll('*[' + attr + ']');
27561         for (var i =0;i<ar.length;i++) {
27562             ar[i].removeAttribute(attr);
27563         }
27564     }
27565         
27566         
27567         
27568     
27569 });
27570 /***
27571  * This is based loosely on tinymce 
27572  * @class Roo.htmleditor.TidySerializer
27573  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27574  * @constructor
27575  * @method Serializer
27576  * @param {Object} settings Name/value settings object.
27577  */
27578
27579
27580 Roo.htmleditor.TidySerializer = function(settings)
27581 {
27582     Roo.apply(this, settings);
27583     
27584     this.writer = new Roo.htmleditor.TidyWriter(settings);
27585     
27586     
27587
27588 };
27589 Roo.htmleditor.TidySerializer.prototype = {
27590     
27591     /**
27592      * @param {boolean} inner do the inner of the node.
27593      */
27594     inner : false,
27595     
27596     writer : false,
27597     
27598     /**
27599     * Serializes the specified node into a string.
27600     *
27601     * @example
27602     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27603     * @method serialize
27604     * @param {DomElement} node Node instance to serialize.
27605     * @return {String} String with HTML based on DOM tree.
27606     */
27607     serialize : function(node) {
27608         
27609         // = settings.validate;
27610         var writer = this.writer;
27611         var self  = this;
27612         this.handlers = {
27613             // #text
27614             3: function(node) {
27615                 
27616                 writer.text(node.nodeValue, node);
27617             },
27618             // #comment
27619             8: function(node) {
27620                 writer.comment(node.nodeValue);
27621             },
27622             // Processing instruction
27623             7: function(node) {
27624                 writer.pi(node.name, node.nodeValue);
27625             },
27626             // Doctype
27627             10: function(node) {
27628                 writer.doctype(node.nodeValue);
27629             },
27630             // CDATA
27631             4: function(node) {
27632                 writer.cdata(node.nodeValue);
27633             },
27634             // Document fragment
27635             11: function(node) {
27636                 node = node.firstChild;
27637                 if (!node) {
27638                     return;
27639                 }
27640                 while(node) {
27641                     self.walk(node);
27642                     node = node.nextSibling
27643                 }
27644             }
27645         };
27646         writer.reset();
27647         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27648         return writer.getContent();
27649     },
27650
27651     walk: function(node)
27652     {
27653         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27654             handler = this.handlers[node.nodeType];
27655             
27656         if (handler) {
27657             handler(node);
27658             return;
27659         }
27660     
27661         var name = node.nodeName;
27662         var isEmpty = node.childNodes.length < 1;
27663       
27664         var writer = this.writer;
27665         var attrs = node.attributes;
27666         // Sort attributes
27667         
27668         writer.start(node.nodeName, attrs, isEmpty, node);
27669         if (isEmpty) {
27670             return;
27671         }
27672         node = node.firstChild;
27673         if (!node) {
27674             writer.end(name);
27675             return;
27676         }
27677         while (node) {
27678             this.walk(node);
27679             node = node.nextSibling;
27680         }
27681         writer.end(name);
27682         
27683     
27684     }
27685     // Serialize element and treat all non elements as fragments
27686    
27687 }; 
27688
27689 /***
27690  * This is based loosely on tinymce 
27691  * @class Roo.htmleditor.TidyWriter
27692  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27693  *
27694  * Known issues?
27695  * - not tested much with 'PRE' formated elements.
27696  * 
27697  *
27698  *
27699  */
27700
27701 Roo.htmleditor.TidyWriter = function(settings)
27702 {
27703     
27704     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27705     Roo.apply(this, settings);
27706     this.html = [];
27707     this.state = [];
27708      
27709     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27710   
27711 }
27712 Roo.htmleditor.TidyWriter.prototype = {
27713
27714  
27715     state : false,
27716     
27717     indent :  '  ',
27718     
27719     // part of state...
27720     indentstr : '',
27721     in_pre: false,
27722     in_inline : false,
27723     last_inline : false,
27724     encode : false,
27725      
27726     
27727             /**
27728     * Writes the a start element such as <p id="a">.
27729     *
27730     * @method start
27731     * @param {String} name Name of the element.
27732     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27733     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27734     */
27735     start: function(name, attrs, empty, node)
27736     {
27737         var i, l, attr, value;
27738         
27739         // there are some situations where adding line break && indentation will not work. will not work.
27740         // <span / b / i ... formating?
27741         
27742         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27743         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27744         
27745         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27746         
27747         var add_lb = name == 'BR' ? false : in_inline;
27748         
27749         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27750             i_inline = false;
27751         }
27752
27753         var indentstr =  this.indentstr;
27754         
27755         // e_inline = elements that can be inline, but still allow \n before and after?
27756         // only 'BR' ??? any others?
27757         
27758         // ADD LINE BEFORE tage
27759         if (!this.in_pre) {
27760             if (in_inline) {
27761                 //code
27762                 if (name == 'BR') {
27763                     this.addLine();
27764                 } else if (this.lastElementEndsWS()) {
27765                     this.addLine();
27766                 } else{
27767                     // otherwise - no new line. (and dont indent.)
27768                     indentstr = '';
27769                 }
27770                 
27771             } else {
27772                 this.addLine();
27773             }
27774         } else {
27775             indentstr = '';
27776         }
27777         
27778         this.html.push(indentstr + '<', name.toLowerCase());
27779         
27780         if (attrs) {
27781             for (i = 0, l = attrs.length; i < l; i++) {
27782                 attr = attrs[i];
27783                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27784             }
27785         }
27786      
27787         if (empty) {
27788             if (is_short) {
27789                 this.html[this.html.length] = '/>';
27790             } else {
27791                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27792             }
27793             var e_inline = name == 'BR' ? false : this.in_inline;
27794             
27795             if (!e_inline && !this.in_pre) {
27796                 this.addLine();
27797             }
27798             return;
27799         
27800         }
27801         // not empty..
27802         this.html[this.html.length] = '>';
27803         
27804         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27805         /*
27806         if (!in_inline && !in_pre) {
27807             var cn = node.firstChild;
27808             while(cn) {
27809                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27810                     in_inline = true
27811                     break;
27812                 }
27813                 cn = cn.nextSibling;
27814             }
27815              
27816         }
27817         */
27818         
27819         
27820         this.pushState({
27821             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27822             in_pre : in_pre,
27823             in_inline :  in_inline
27824         });
27825         // add a line after if we are not in a
27826         
27827         if (!in_inline && !in_pre) {
27828             this.addLine();
27829         }
27830         
27831             
27832          
27833         
27834     },
27835     
27836     lastElementEndsWS : function()
27837     {
27838         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27839         if (value === false) {
27840             return true;
27841         }
27842         return value.match(/\s+$/);
27843         
27844     },
27845     
27846     /**
27847      * Writes the a end element such as </p>.
27848      *
27849      * @method end
27850      * @param {String} name Name of the element.
27851      */
27852     end: function(name) {
27853         var value;
27854         this.popState();
27855         var indentstr = '';
27856         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27857         
27858         if (!this.in_pre && !in_inline) {
27859             this.addLine();
27860             indentstr  = this.indentstr;
27861         }
27862         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27863         this.last_inline = in_inline;
27864         
27865         // pop the indent state..
27866     },
27867     /**
27868      * Writes a text node.
27869      *
27870      * In pre - we should not mess with the contents.
27871      * 
27872      *
27873      * @method text
27874      * @param {String} text String to write out.
27875      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27876      */
27877     text: function(in_text, node)
27878     {
27879         // if not in whitespace critical
27880         if (in_text.length < 1) {
27881             return;
27882         }
27883         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27884         
27885         if (this.in_pre) {
27886             this.html[this.html.length] =  text;
27887             return;   
27888         }
27889         
27890         if (this.in_inline) {
27891             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27892             if (text != ' ') {
27893                 text = text.replace(/\s+/,' ');  // all white space to single white space
27894                 
27895                     
27896                 // if next tag is '<BR>', then we can trim right..
27897                 if (node.nextSibling &&
27898                     node.nextSibling.nodeType == 1 &&
27899                     node.nextSibling.nodeName == 'BR' )
27900                 {
27901                     text = text.replace(/\s+$/g,'');
27902                 }
27903                 // if previous tag was a BR, we can also trim..
27904                 if (node.previousSibling &&
27905                     node.previousSibling.nodeType == 1 &&
27906                     node.previousSibling.nodeName == 'BR' )
27907                 {
27908                     text = this.indentstr +  text.replace(/^\s+/g,'');
27909                 }
27910                 if (text.match(/\n/)) {
27911                     text = text.replace(
27912                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27913                     );
27914                     // remoeve the last whitespace / line break.
27915                     text = text.replace(/\n\s+$/,'');
27916                 }
27917                 // repace long lines
27918                 
27919             }
27920              
27921             this.html[this.html.length] =  text;
27922             return;   
27923         }
27924         // see if previous element was a inline element.
27925         var indentstr = this.indentstr;
27926    
27927         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27928         
27929         // should trim left?
27930         if (node.previousSibling &&
27931             node.previousSibling.nodeType == 1 &&
27932             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27933         {
27934             indentstr = '';
27935             
27936         } else {
27937             this.addLine();
27938             text = text.replace(/^\s+/,''); // trim left
27939           
27940         }
27941         // should trim right?
27942         if (node.nextSibling &&
27943             node.nextSibling.nodeType == 1 &&
27944             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27945         {
27946           // noop
27947             
27948         }  else {
27949             text = text.replace(/\s+$/,''); // trim right
27950         }
27951          
27952               
27953         
27954         
27955         
27956         if (text.length < 1) {
27957             return;
27958         }
27959         if (!text.match(/\n/)) {
27960             this.html.push(indentstr + text);
27961             return;
27962         }
27963         
27964         text = this.indentstr + text.replace(
27965             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27966         );
27967         // remoeve the last whitespace / line break.
27968         text = text.replace(/\s+$/,''); 
27969         
27970         this.html.push(text);
27971         
27972         // split and indent..
27973         
27974         
27975     },
27976     /**
27977      * Writes a cdata node such as <![CDATA[data]]>.
27978      *
27979      * @method cdata
27980      * @param {String} text String to write out inside the cdata.
27981      */
27982     cdata: function(text) {
27983         this.html.push('<![CDATA[', text, ']]>');
27984     },
27985     /**
27986     * Writes a comment node such as <!-- Comment -->.
27987     *
27988     * @method cdata
27989     * @param {String} text String to write out inside the comment.
27990     */
27991    comment: function(text) {
27992        this.html.push('<!--', text, '-->');
27993    },
27994     /**
27995      * Writes a PI node such as <?xml attr="value" ?>.
27996      *
27997      * @method pi
27998      * @param {String} name Name of the pi.
27999      * @param {String} text String to write out inside the pi.
28000      */
28001     pi: function(name, text) {
28002         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28003         this.indent != '' && this.html.push('\n');
28004     },
28005     /**
28006      * Writes a doctype node such as <!DOCTYPE data>.
28007      *
28008      * @method doctype
28009      * @param {String} text String to write out inside the doctype.
28010      */
28011     doctype: function(text) {
28012         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28013     },
28014     /**
28015      * Resets the internal buffer if one wants to reuse the writer.
28016      *
28017      * @method reset
28018      */
28019     reset: function() {
28020         this.html.length = 0;
28021         this.state = [];
28022         this.pushState({
28023             indentstr : '',
28024             in_pre : false, 
28025             in_inline : false
28026         })
28027     },
28028     /**
28029      * Returns the contents that got serialized.
28030      *
28031      * @method getContent
28032      * @return {String} HTML contents that got written down.
28033      */
28034     getContent: function() {
28035         return this.html.join('').replace(/\n$/, '');
28036     },
28037     
28038     pushState : function(cfg)
28039     {
28040         this.state.push(cfg);
28041         Roo.apply(this, cfg);
28042     },
28043     
28044     popState : function()
28045     {
28046         if (this.state.length < 1) {
28047             return; // nothing to push
28048         }
28049         var cfg = {
28050             in_pre: false,
28051             indentstr : ''
28052         };
28053         this.state.pop();
28054         if (this.state.length > 0) {
28055             cfg = this.state[this.state.length-1]; 
28056         }
28057         Roo.apply(this, cfg);
28058     },
28059     
28060     addLine: function()
28061     {
28062         if (this.html.length < 1) {
28063             return;
28064         }
28065         
28066         
28067         var value = this.html[this.html.length - 1];
28068         if (value.length > 0 && '\n' !== value) {
28069             this.html.push('\n');
28070         }
28071     }
28072     
28073     
28074 //'pre script noscript style textarea video audio iframe object code'
28075 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28076 // inline 
28077 };
28078
28079 Roo.htmleditor.TidyWriter.inline_elements = [
28080         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28081         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28082 ];
28083 Roo.htmleditor.TidyWriter.shortend_elements = [
28084     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28085     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28086 ];
28087
28088 Roo.htmleditor.TidyWriter.whitespace_elements = [
28089     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28090 ];/***
28091  * This is based loosely on tinymce 
28092  * @class Roo.htmleditor.TidyEntities
28093  * @static
28094  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28095  *
28096  * Not 100% sure this is actually used or needed.
28097  */
28098
28099 Roo.htmleditor.TidyEntities = {
28100     
28101     /**
28102      * initialize data..
28103      */
28104     init : function (){
28105      
28106         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28107        
28108     },
28109
28110
28111     buildEntitiesLookup: function(items, radix) {
28112         var i, chr, entity, lookup = {};
28113         if (!items) {
28114             return {};
28115         }
28116         items = typeof(items) == 'string' ? items.split(',') : items;
28117         radix = radix || 10;
28118         // Build entities lookup table
28119         for (i = 0; i < items.length; i += 2) {
28120             chr = String.fromCharCode(parseInt(items[i], radix));
28121             // Only add non base entities
28122             if (!this.baseEntities[chr]) {
28123                 entity = '&' + items[i + 1] + ';';
28124                 lookup[chr] = entity;
28125                 lookup[entity] = chr;
28126             }
28127         }
28128         return lookup;
28129         
28130     },
28131     
28132     asciiMap : {
28133             128: '€',
28134             130: '‚',
28135             131: 'ƒ',
28136             132: '„',
28137             133: '…',
28138             134: '†',
28139             135: '‡',
28140             136: 'ˆ',
28141             137: '‰',
28142             138: 'Š',
28143             139: '‹',
28144             140: 'Œ',
28145             142: 'Ž',
28146             145: '‘',
28147             146: '’',
28148             147: '“',
28149             148: '”',
28150             149: '•',
28151             150: '–',
28152             151: '—',
28153             152: '˜',
28154             153: '™',
28155             154: 'š',
28156             155: '›',
28157             156: 'œ',
28158             158: 'ž',
28159             159: 'Ÿ'
28160     },
28161     // Raw entities
28162     baseEntities : {
28163         '"': '&quot;',
28164         // Needs to be escaped since the YUI compressor would otherwise break the code
28165         '\'': '&#39;',
28166         '<': '&lt;',
28167         '>': '&gt;',
28168         '&': '&amp;',
28169         '`': '&#96;'
28170     },
28171     // Reverse lookup table for raw entities
28172     reverseEntities : {
28173         '&lt;': '<',
28174         '&gt;': '>',
28175         '&amp;': '&',
28176         '&quot;': '"',
28177         '&apos;': '\''
28178     },
28179     
28180     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28181     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28182     rawCharsRegExp : /[<>&\"\']/g,
28183     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28184     namedEntities  : false,
28185     namedEntitiesData : [ 
28186         '50',
28187         'nbsp',
28188         '51',
28189         'iexcl',
28190         '52',
28191         'cent',
28192         '53',
28193         'pound',
28194         '54',
28195         'curren',
28196         '55',
28197         'yen',
28198         '56',
28199         'brvbar',
28200         '57',
28201         'sect',
28202         '58',
28203         'uml',
28204         '59',
28205         'copy',
28206         '5a',
28207         'ordf',
28208         '5b',
28209         'laquo',
28210         '5c',
28211         'not',
28212         '5d',
28213         'shy',
28214         '5e',
28215         'reg',
28216         '5f',
28217         'macr',
28218         '5g',
28219         'deg',
28220         '5h',
28221         'plusmn',
28222         '5i',
28223         'sup2',
28224         '5j',
28225         'sup3',
28226         '5k',
28227         'acute',
28228         '5l',
28229         'micro',
28230         '5m',
28231         'para',
28232         '5n',
28233         'middot',
28234         '5o',
28235         'cedil',
28236         '5p',
28237         'sup1',
28238         '5q',
28239         'ordm',
28240         '5r',
28241         'raquo',
28242         '5s',
28243         'frac14',
28244         '5t',
28245         'frac12',
28246         '5u',
28247         'frac34',
28248         '5v',
28249         'iquest',
28250         '60',
28251         'Agrave',
28252         '61',
28253         'Aacute',
28254         '62',
28255         'Acirc',
28256         '63',
28257         'Atilde',
28258         '64',
28259         'Auml',
28260         '65',
28261         'Aring',
28262         '66',
28263         'AElig',
28264         '67',
28265         'Ccedil',
28266         '68',
28267         'Egrave',
28268         '69',
28269         'Eacute',
28270         '6a',
28271         'Ecirc',
28272         '6b',
28273         'Euml',
28274         '6c',
28275         'Igrave',
28276         '6d',
28277         'Iacute',
28278         '6e',
28279         'Icirc',
28280         '6f',
28281         'Iuml',
28282         '6g',
28283         'ETH',
28284         '6h',
28285         'Ntilde',
28286         '6i',
28287         'Ograve',
28288         '6j',
28289         'Oacute',
28290         '6k',
28291         'Ocirc',
28292         '6l',
28293         'Otilde',
28294         '6m',
28295         'Ouml',
28296         '6n',
28297         'times',
28298         '6o',
28299         'Oslash',
28300         '6p',
28301         'Ugrave',
28302         '6q',
28303         'Uacute',
28304         '6r',
28305         'Ucirc',
28306         '6s',
28307         'Uuml',
28308         '6t',
28309         'Yacute',
28310         '6u',
28311         'THORN',
28312         '6v',
28313         'szlig',
28314         '70',
28315         'agrave',
28316         '71',
28317         'aacute',
28318         '72',
28319         'acirc',
28320         '73',
28321         'atilde',
28322         '74',
28323         'auml',
28324         '75',
28325         'aring',
28326         '76',
28327         'aelig',
28328         '77',
28329         'ccedil',
28330         '78',
28331         'egrave',
28332         '79',
28333         'eacute',
28334         '7a',
28335         'ecirc',
28336         '7b',
28337         'euml',
28338         '7c',
28339         'igrave',
28340         '7d',
28341         'iacute',
28342         '7e',
28343         'icirc',
28344         '7f',
28345         'iuml',
28346         '7g',
28347         'eth',
28348         '7h',
28349         'ntilde',
28350         '7i',
28351         'ograve',
28352         '7j',
28353         'oacute',
28354         '7k',
28355         'ocirc',
28356         '7l',
28357         'otilde',
28358         '7m',
28359         'ouml',
28360         '7n',
28361         'divide',
28362         '7o',
28363         'oslash',
28364         '7p',
28365         'ugrave',
28366         '7q',
28367         'uacute',
28368         '7r',
28369         'ucirc',
28370         '7s',
28371         'uuml',
28372         '7t',
28373         'yacute',
28374         '7u',
28375         'thorn',
28376         '7v',
28377         'yuml',
28378         'ci',
28379         'fnof',
28380         'sh',
28381         'Alpha',
28382         'si',
28383         'Beta',
28384         'sj',
28385         'Gamma',
28386         'sk',
28387         'Delta',
28388         'sl',
28389         'Epsilon',
28390         'sm',
28391         'Zeta',
28392         'sn',
28393         'Eta',
28394         'so',
28395         'Theta',
28396         'sp',
28397         'Iota',
28398         'sq',
28399         'Kappa',
28400         'sr',
28401         'Lambda',
28402         'ss',
28403         'Mu',
28404         'st',
28405         'Nu',
28406         'su',
28407         'Xi',
28408         'sv',
28409         'Omicron',
28410         't0',
28411         'Pi',
28412         't1',
28413         'Rho',
28414         't3',
28415         'Sigma',
28416         't4',
28417         'Tau',
28418         't5',
28419         'Upsilon',
28420         't6',
28421         'Phi',
28422         't7',
28423         'Chi',
28424         't8',
28425         'Psi',
28426         't9',
28427         'Omega',
28428         'th',
28429         'alpha',
28430         'ti',
28431         'beta',
28432         'tj',
28433         'gamma',
28434         'tk',
28435         'delta',
28436         'tl',
28437         'epsilon',
28438         'tm',
28439         'zeta',
28440         'tn',
28441         'eta',
28442         'to',
28443         'theta',
28444         'tp',
28445         'iota',
28446         'tq',
28447         'kappa',
28448         'tr',
28449         'lambda',
28450         'ts',
28451         'mu',
28452         'tt',
28453         'nu',
28454         'tu',
28455         'xi',
28456         'tv',
28457         'omicron',
28458         'u0',
28459         'pi',
28460         'u1',
28461         'rho',
28462         'u2',
28463         'sigmaf',
28464         'u3',
28465         'sigma',
28466         'u4',
28467         'tau',
28468         'u5',
28469         'upsilon',
28470         'u6',
28471         'phi',
28472         'u7',
28473         'chi',
28474         'u8',
28475         'psi',
28476         'u9',
28477         'omega',
28478         'uh',
28479         'thetasym',
28480         'ui',
28481         'upsih',
28482         'um',
28483         'piv',
28484         '812',
28485         'bull',
28486         '816',
28487         'hellip',
28488         '81i',
28489         'prime',
28490         '81j',
28491         'Prime',
28492         '81u',
28493         'oline',
28494         '824',
28495         'frasl',
28496         '88o',
28497         'weierp',
28498         '88h',
28499         'image',
28500         '88s',
28501         'real',
28502         '892',
28503         'trade',
28504         '89l',
28505         'alefsym',
28506         '8cg',
28507         'larr',
28508         '8ch',
28509         'uarr',
28510         '8ci',
28511         'rarr',
28512         '8cj',
28513         'darr',
28514         '8ck',
28515         'harr',
28516         '8dl',
28517         'crarr',
28518         '8eg',
28519         'lArr',
28520         '8eh',
28521         'uArr',
28522         '8ei',
28523         'rArr',
28524         '8ej',
28525         'dArr',
28526         '8ek',
28527         'hArr',
28528         '8g0',
28529         'forall',
28530         '8g2',
28531         'part',
28532         '8g3',
28533         'exist',
28534         '8g5',
28535         'empty',
28536         '8g7',
28537         'nabla',
28538         '8g8',
28539         'isin',
28540         '8g9',
28541         'notin',
28542         '8gb',
28543         'ni',
28544         '8gf',
28545         'prod',
28546         '8gh',
28547         'sum',
28548         '8gi',
28549         'minus',
28550         '8gn',
28551         'lowast',
28552         '8gq',
28553         'radic',
28554         '8gt',
28555         'prop',
28556         '8gu',
28557         'infin',
28558         '8h0',
28559         'ang',
28560         '8h7',
28561         'and',
28562         '8h8',
28563         'or',
28564         '8h9',
28565         'cap',
28566         '8ha',
28567         'cup',
28568         '8hb',
28569         'int',
28570         '8hk',
28571         'there4',
28572         '8hs',
28573         'sim',
28574         '8i5',
28575         'cong',
28576         '8i8',
28577         'asymp',
28578         '8j0',
28579         'ne',
28580         '8j1',
28581         'equiv',
28582         '8j4',
28583         'le',
28584         '8j5',
28585         'ge',
28586         '8k2',
28587         'sub',
28588         '8k3',
28589         'sup',
28590         '8k4',
28591         'nsub',
28592         '8k6',
28593         'sube',
28594         '8k7',
28595         'supe',
28596         '8kl',
28597         'oplus',
28598         '8kn',
28599         'otimes',
28600         '8l5',
28601         'perp',
28602         '8m5',
28603         'sdot',
28604         '8o8',
28605         'lceil',
28606         '8o9',
28607         'rceil',
28608         '8oa',
28609         'lfloor',
28610         '8ob',
28611         'rfloor',
28612         '8p9',
28613         'lang',
28614         '8pa',
28615         'rang',
28616         '9ea',
28617         'loz',
28618         '9j0',
28619         'spades',
28620         '9j3',
28621         'clubs',
28622         '9j5',
28623         'hearts',
28624         '9j6',
28625         'diams',
28626         'ai',
28627         'OElig',
28628         'aj',
28629         'oelig',
28630         'b0',
28631         'Scaron',
28632         'b1',
28633         'scaron',
28634         'bo',
28635         'Yuml',
28636         'm6',
28637         'circ',
28638         'ms',
28639         'tilde',
28640         '802',
28641         'ensp',
28642         '803',
28643         'emsp',
28644         '809',
28645         'thinsp',
28646         '80c',
28647         'zwnj',
28648         '80d',
28649         'zwj',
28650         '80e',
28651         'lrm',
28652         '80f',
28653         'rlm',
28654         '80j',
28655         'ndash',
28656         '80k',
28657         'mdash',
28658         '80o',
28659         'lsquo',
28660         '80p',
28661         'rsquo',
28662         '80q',
28663         'sbquo',
28664         '80s',
28665         'ldquo',
28666         '80t',
28667         'rdquo',
28668         '80u',
28669         'bdquo',
28670         '810',
28671         'dagger',
28672         '811',
28673         'Dagger',
28674         '81g',
28675         'permil',
28676         '81p',
28677         'lsaquo',
28678         '81q',
28679         'rsaquo',
28680         '85c',
28681         'euro'
28682     ],
28683
28684          
28685     /**
28686      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28687      *
28688      * @method encodeRaw
28689      * @param {String} text Text to encode.
28690      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28691      * @return {String} Entity encoded text.
28692      */
28693     encodeRaw: function(text, attr)
28694     {
28695         var t = this;
28696         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28697             return t.baseEntities[chr] || chr;
28698         });
28699     },
28700     /**
28701      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28702      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28703      * and is exposed as the DOMUtils.encode function.
28704      *
28705      * @method encodeAllRaw
28706      * @param {String} text Text to encode.
28707      * @return {String} Entity encoded text.
28708      */
28709     encodeAllRaw: function(text) {
28710         var t = this;
28711         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28712             return t.baseEntities[chr] || chr;
28713         });
28714     },
28715     /**
28716      * Encodes the specified string using numeric entities. The core entities will be
28717      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28718      *
28719      * @method encodeNumeric
28720      * @param {String} text Text to encode.
28721      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28722      * @return {String} Entity encoded text.
28723      */
28724     encodeNumeric: function(text, attr) {
28725         var t = this;
28726         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28727             // Multi byte sequence convert it to a single entity
28728             if (chr.length > 1) {
28729                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28730             }
28731             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28732         });
28733     },
28734     /**
28735      * Encodes the specified string using named entities. The core entities will be encoded
28736      * as named ones but all non lower ascii characters will be encoded into named entities.
28737      *
28738      * @method encodeNamed
28739      * @param {String} text Text to encode.
28740      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28741      * @param {Object} entities Optional parameter with entities to use.
28742      * @return {String} Entity encoded text.
28743      */
28744     encodeNamed: function(text, attr, entities) {
28745         var t = this;
28746         entities = entities || this.namedEntities;
28747         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28748             return t.baseEntities[chr] || entities[chr] || chr;
28749         });
28750     },
28751     /**
28752      * Returns an encode function based on the name(s) and it's optional entities.
28753      *
28754      * @method getEncodeFunc
28755      * @param {String} name Comma separated list of encoders for example named,numeric.
28756      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28757      * @return {function} Encode function to be used.
28758      */
28759     getEncodeFunc: function(name, entities) {
28760         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28761         var t = this;
28762         function encodeNamedAndNumeric(text, attr) {
28763             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28764                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28765             });
28766         }
28767
28768         function encodeCustomNamed(text, attr) {
28769             return t.encodeNamed(text, attr, entities);
28770         }
28771         // Replace + with , to be compatible with previous TinyMCE versions
28772         name = this.makeMap(name.replace(/\+/g, ','));
28773         // Named and numeric encoder
28774         if (name.named && name.numeric) {
28775             return this.encodeNamedAndNumeric;
28776         }
28777         // Named encoder
28778         if (name.named) {
28779             // Custom names
28780             if (entities) {
28781                 return encodeCustomNamed;
28782             }
28783             return this.encodeNamed;
28784         }
28785         // Numeric
28786         if (name.numeric) {
28787             return this.encodeNumeric;
28788         }
28789         // Raw encoder
28790         return this.encodeRaw;
28791     },
28792     /**
28793      * Decodes the specified string, this will replace entities with raw UTF characters.
28794      *
28795      * @method decode
28796      * @param {String} text Text to entity decode.
28797      * @return {String} Entity decoded string.
28798      */
28799     decode: function(text)
28800     {
28801         var  t = this;
28802         return text.replace(this.entityRegExp, function(all, numeric) {
28803             if (numeric) {
28804                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28805                 // Support upper UTF
28806                 if (numeric > 65535) {
28807                     numeric -= 65536;
28808                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28809                 }
28810                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28811             }
28812             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28813         });
28814     },
28815     nativeDecode : function (text) {
28816         return text;
28817     },
28818     makeMap : function (items, delim, map) {
28819                 var i;
28820                 items = items || [];
28821                 delim = delim || ',';
28822                 if (typeof items == "string") {
28823                         items = items.split(delim);
28824                 }
28825                 map = map || {};
28826                 i = items.length;
28827                 while (i--) {
28828                         map[items[i]] = {};
28829                 }
28830                 return map;
28831         }
28832 };
28833     
28834     
28835     
28836 Roo.htmleditor.TidyEntities.init();
28837 /**
28838  * @class Roo.htmleditor.KeyEnter
28839  * Handle Enter press..
28840  * @cfg {Roo.HtmlEditorCore} core the editor.
28841  * @constructor
28842  * Create a new Filter.
28843  * @param {Object} config Configuration options
28844  */
28845
28846
28847
28848
28849
28850 Roo.htmleditor.KeyEnter = function(cfg) {
28851     Roo.apply(this, cfg);
28852     // this does not actually call walk as it's really just a abstract class
28853  
28854     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28855 }
28856
28857 //Roo.htmleditor.KeyEnter.i = 0;
28858
28859
28860 Roo.htmleditor.KeyEnter.prototype = {
28861     
28862     core : false,
28863     
28864     keypress : function(e)
28865     {
28866         if (e.charCode != 13 && e.charCode != 10) {
28867             Roo.log([e.charCode,e]);
28868             return true;
28869         }
28870         e.preventDefault();
28871         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28872         var doc = this.core.doc;
28873           //add a new line
28874        
28875     
28876         var sel = this.core.getSelection();
28877         var range = sel.getRangeAt(0);
28878         var n = range.commonAncestorContainer;
28879         var pc = range.closest([ 'ol', 'ul']);
28880         var pli = range.closest('li');
28881         if (!pc || e.ctrlKey) {
28882             // on it list, or ctrl pressed.
28883             if (!e.ctrlKey) {
28884                 sel.insertNode('br', 'after'); 
28885             } else {
28886                 // only do this if we have ctrl key..
28887                 var br = doc.createElement('br');
28888                 br.className = 'clear';
28889                 br.setAttribute('style', 'clear: both');
28890                 sel.insertNode(br, 'after'); 
28891             }
28892             
28893          
28894             this.core.undoManager.addEvent();
28895             this.core.fireEditorEvent(e);
28896             return false;
28897         }
28898         
28899         // deal with <li> insetion
28900         if (pli.innerText.trim() == '' &&
28901             pli.previousSibling &&
28902             pli.previousSibling.nodeName == 'LI' &&
28903             pli.previousSibling.innerText.trim() ==  '') {
28904             pli.parentNode.removeChild(pli.previousSibling);
28905             sel.cursorAfter(pc);
28906             this.core.undoManager.addEvent();
28907             this.core.fireEditorEvent(e);
28908             return false;
28909         }
28910     
28911         var li = doc.createElement('LI');
28912         li.innerHTML = '&nbsp;';
28913         if (!pli || !pli.firstSibling) {
28914             pc.appendChild(li);
28915         } else {
28916             pli.parentNode.insertBefore(li, pli.firstSibling);
28917         }
28918         sel.cursorText (li.firstChild);
28919       
28920         this.core.undoManager.addEvent();
28921         this.core.fireEditorEvent(e);
28922
28923         return false;
28924         
28925     
28926         
28927         
28928          
28929     }
28930 };
28931      
28932 /**
28933  * @class Roo.htmleditor.Block
28934  * Base class for html editor blocks - do not use it directly .. extend it..
28935  * @cfg {DomElement} node The node to apply stuff to.
28936  * @cfg {String} friendly_name the name that appears in the context bar about this block
28937  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28938  
28939  * @constructor
28940  * Create a new Filter.
28941  * @param {Object} config Configuration options
28942  */
28943
28944 Roo.htmleditor.Block  = function(cfg)
28945 {
28946     // do nothing .. should not be called really.
28947 }
28948 /**
28949  * factory method to get the block from an element (using cache if necessary)
28950  * @static
28951  * @param {HtmlElement} the dom element
28952  */
28953 Roo.htmleditor.Block.factory = function(node)
28954 {
28955     var cc = Roo.htmleditor.Block.cache;
28956     var id = Roo.get(node).id;
28957     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28958         Roo.htmleditor.Block.cache[id].readElement(node);
28959         return Roo.htmleditor.Block.cache[id];
28960     }
28961     var db  = node.getAttribute('data-block');
28962     if (!db) {
28963         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28964     }
28965     var cls = Roo.htmleditor['Block' + db];
28966     if (typeof(cls) == 'undefined') {
28967         //Roo.log(node.getAttribute('data-block'));
28968         Roo.log("OOps missing block : " + 'Block' + db);
28969         return false;
28970     }
28971     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28972     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28973 };
28974
28975 /**
28976  * initalize all Elements from content that are 'blockable'
28977  * @static
28978  * @param the body element
28979  */
28980 Roo.htmleditor.Block.initAll = function(body, type)
28981 {
28982     if (typeof(type) == 'undefined') {
28983         var ia = Roo.htmleditor.Block.initAll;
28984         ia(body,'table');
28985         ia(body,'td');
28986         ia(body,'figure');
28987         return;
28988     }
28989     Roo.each(Roo.get(body).query(type), function(e) {
28990         Roo.htmleditor.Block.factory(e);    
28991     },this);
28992 };
28993 // question goes here... do we need to clear out this cache sometimes?
28994 // or show we make it relivant to the htmleditor.
28995 Roo.htmleditor.Block.cache = {};
28996
28997 Roo.htmleditor.Block.prototype = {
28998     
28999     node : false,
29000     
29001      // used by context menu
29002     friendly_name : 'Based Block',
29003     
29004     // text for button to delete this element
29005     deleteTitle : false,
29006     
29007     context : false,
29008     /**
29009      * Update a node with values from this object
29010      * @param {DomElement} node
29011      */
29012     updateElement : function(node)
29013     {
29014         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29015     },
29016      /**
29017      * convert to plain HTML for calling insertAtCursor..
29018      */
29019     toHTML : function()
29020     {
29021         return Roo.DomHelper.markup(this.toObject());
29022     },
29023     /**
29024      * used by readEleemnt to extract data from a node
29025      * may need improving as it's pretty basic
29026      
29027      * @param {DomElement} node
29028      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29029      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29030      * @param {String} style the style property - eg. text-align
29031      */
29032     getVal : function(node, tag, attr, style)
29033     {
29034         var n = node;
29035         if (tag !== true && n.tagName != tag.toUpperCase()) {
29036             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29037             // but kiss for now.
29038             n = node.getElementsByTagName(tag).item(0);
29039         }
29040         if (!n) {
29041             return '';
29042         }
29043         if (attr === false) {
29044             return n;
29045         }
29046         if (attr == 'html') {
29047             return n.innerHTML;
29048         }
29049         if (attr == 'style') {
29050             return n.style[style]; 
29051         }
29052         
29053         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29054             
29055     },
29056     /**
29057      * create a DomHelper friendly object - for use with 
29058      * Roo.DomHelper.markup / overwrite / etc..
29059      * (override this)
29060      */
29061     toObject : function()
29062     {
29063         return {};
29064     },
29065       /**
29066      * Read a node that has a 'data-block' property - and extract the values from it.
29067      * @param {DomElement} node - the node
29068      */
29069     readElement : function(node)
29070     {
29071         
29072     } 
29073     
29074     
29075 };
29076
29077  
29078
29079 /**
29080  * @class Roo.htmleditor.BlockFigure
29081  * Block that has an image and a figcaption
29082  * @cfg {String} image_src the url for the image
29083  * @cfg {String} align (left|right) alignment for the block default left
29084  * @cfg {String} caption the text to appear below  (and in the alt tag)
29085  * @cfg {String} caption_display (block|none) display or not the caption
29086  * @cfg {String|number} image_width the width of the image number or %?
29087  * @cfg {String|number} image_height the height of the image number or %?
29088  * 
29089  * @constructor
29090  * Create a new Filter.
29091  * @param {Object} config Configuration options
29092  */
29093
29094 Roo.htmleditor.BlockFigure = function(cfg)
29095 {
29096     if (cfg.node) {
29097         this.readElement(cfg.node);
29098         this.updateElement(cfg.node);
29099     }
29100     Roo.apply(this, cfg);
29101 }
29102 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29103  
29104     
29105     // setable values.
29106     image_src: '',
29107     align: 'center',
29108     caption : '',
29109     caption_display : 'block',
29110     width : '100%',
29111     cls : '',
29112     href: '',
29113     video_url : '',
29114     
29115     // margin: '2%', not used
29116     
29117     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29118
29119     
29120     // used by context menu
29121     friendly_name : 'Image with caption',
29122     deleteTitle : "Delete Image and Caption",
29123     
29124     contextMenu : function(toolbar)
29125     {
29126         
29127         var block = function() {
29128             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29129         };
29130         
29131         
29132         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29133         
29134         var syncValue = toolbar.editorcore.syncValue;
29135         
29136         var fields = {};
29137         
29138         return [
29139              {
29140                 xtype : 'TextItem',
29141                 text : "Source: ",
29142                 xns : rooui.Toolbar  //Boostrap?
29143             },
29144             {
29145                 xtype : 'Button',
29146                 text: 'Change Image URL',
29147                  
29148                 listeners : {
29149                     click: function (btn, state)
29150                     {
29151                         var b = block();
29152                         
29153                         Roo.MessageBox.show({
29154                             title : "Image Source URL",
29155                             msg : "Enter the url for the image",
29156                             buttons: Roo.MessageBox.OKCANCEL,
29157                             fn: function(btn, val){
29158                                 if (btn != 'ok') {
29159                                     return;
29160                                 }
29161                                 b.image_src = val;
29162                                 b.updateElement();
29163                                 syncValue();
29164                                 toolbar.editorcore.onEditorEvent();
29165                             },
29166                             minWidth:250,
29167                             prompt:true,
29168                             //multiline: multiline,
29169                             modal : true,
29170                             value : b.image_src
29171                         });
29172                     }
29173                 },
29174                 xns : rooui.Toolbar
29175             },
29176          
29177             {
29178                 xtype : 'Button',
29179                 text: 'Change Link URL',
29180                  
29181                 listeners : {
29182                     click: function (btn, state)
29183                     {
29184                         var b = block();
29185                         
29186                         Roo.MessageBox.show({
29187                             title : "Link URL",
29188                             msg : "Enter the url for the link - leave blank to have no link",
29189                             buttons: Roo.MessageBox.OKCANCEL,
29190                             fn: function(btn, val){
29191                                 if (btn != 'ok') {
29192                                     return;
29193                                 }
29194                                 b.href = val;
29195                                 b.updateElement();
29196                                 syncValue();
29197                                 toolbar.editorcore.onEditorEvent();
29198                             },
29199                             minWidth:250,
29200                             prompt:true,
29201                             //multiline: multiline,
29202                             modal : true,
29203                             value : b.href
29204                         });
29205                     }
29206                 },
29207                 xns : rooui.Toolbar
29208             },
29209             {
29210                 xtype : 'Button',
29211                 text: 'Show Video URL',
29212                  
29213                 listeners : {
29214                     click: function (btn, state)
29215                     {
29216                         Roo.MessageBox.alert("Video URL",
29217                             block().video_url == '' ? 'This image is not linked ot a video' :
29218                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29219                     }
29220                 },
29221                 xns : rooui.Toolbar
29222             },
29223             
29224             
29225             {
29226                 xtype : 'TextItem',
29227                 text : "Width: ",
29228                 xns : rooui.Toolbar  //Boostrap?
29229             },
29230             {
29231                 xtype : 'ComboBox',
29232                 allowBlank : false,
29233                 displayField : 'val',
29234                 editable : true,
29235                 listWidth : 100,
29236                 triggerAction : 'all',
29237                 typeAhead : true,
29238                 valueField : 'val',
29239                 width : 70,
29240                 name : 'width',
29241                 listeners : {
29242                     select : function (combo, r, index)
29243                     {
29244                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29245                         var b = block();
29246                         b.width = r.get('val');
29247                         b.updateElement();
29248                         syncValue();
29249                         toolbar.editorcore.onEditorEvent();
29250                     }
29251                 },
29252                 xns : rooui.form,
29253                 store : {
29254                     xtype : 'SimpleStore',
29255                     data : [
29256                         ['100%'],
29257                         ['80%'],
29258                         ['50%'],
29259                         ['20%'],
29260                         ['10%']
29261                     ],
29262                     fields : [ 'val'],
29263                     xns : Roo.data
29264                 }
29265             },
29266             {
29267                 xtype : 'TextItem',
29268                 text : "Align: ",
29269                 xns : rooui.Toolbar  //Boostrap?
29270             },
29271             {
29272                 xtype : 'ComboBox',
29273                 allowBlank : false,
29274                 displayField : 'val',
29275                 editable : true,
29276                 listWidth : 100,
29277                 triggerAction : 'all',
29278                 typeAhead : true,
29279                 valueField : 'val',
29280                 width : 70,
29281                 name : 'align',
29282                 listeners : {
29283                     select : function (combo, r, index)
29284                     {
29285                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29286                         var b = block();
29287                         b.align = r.get('val');
29288                         b.updateElement();
29289                         syncValue();
29290                         toolbar.editorcore.onEditorEvent();
29291                     }
29292                 },
29293                 xns : rooui.form,
29294                 store : {
29295                     xtype : 'SimpleStore',
29296                     data : [
29297                         ['left'],
29298                         ['right'],
29299                         ['center']
29300                     ],
29301                     fields : [ 'val'],
29302                     xns : Roo.data
29303                 }
29304             },
29305             
29306             
29307             {
29308                 xtype : 'Button',
29309                 text: 'Hide Caption',
29310                 name : 'caption_display',
29311                 pressed : false,
29312                 enableToggle : true,
29313                 setValue : function(v) {
29314                     // this trigger toggle.
29315                      
29316                     this.setText(v ? "Hide Caption" : "Show Caption");
29317                     this.setPressed(v != 'block');
29318                 },
29319                 listeners : {
29320                     toggle: function (btn, state)
29321                     {
29322                         var b  = block();
29323                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29324                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29325                         b.updateElement();
29326                         syncValue();
29327                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29328                         toolbar.editorcore.onEditorEvent();
29329                     }
29330                 },
29331                 xns : rooui.Toolbar
29332             }
29333         ];
29334         
29335     },
29336     /**
29337      * create a DomHelper friendly object - for use with
29338      * Roo.DomHelper.markup / overwrite / etc..
29339      */
29340     toObject : function()
29341     {
29342         var d = document.createElement('div');
29343         d.innerHTML = this.caption;
29344         
29345         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29346         
29347         var iw = this.align == 'center' ? this.width : '100%';
29348         var img =   {
29349             tag : 'img',
29350             contenteditable : 'false',
29351             src : this.image_src,
29352             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29353             style: {
29354                 width : iw,
29355                 maxWidth : iw + ' !important', // this is not getting rendered?
29356                 margin : m  
29357                 
29358             }
29359         };
29360         /*
29361         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29362                     '<a href="{2}">' + 
29363                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29364                     '</a>' + 
29365                 '</div>',
29366         */
29367                 
29368         if (this.href.length > 0) {
29369             img = {
29370                 tag : 'a',
29371                 href: this.href,
29372                 contenteditable : 'true',
29373                 cn : [
29374                     img
29375                 ]
29376             };
29377         }
29378         
29379         
29380         if (this.video_url.length > 0) {
29381             img = {
29382                 tag : 'div',
29383                 cls : this.cls,
29384                 frameborder : 0,
29385                 allowfullscreen : true,
29386                 width : 420,  // these are for video tricks - that we replace the outer
29387                 height : 315,
29388                 src : this.video_url,
29389                 cn : [
29390                     img
29391                 ]
29392             };
29393         }
29394         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29395         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29396         
29397   
29398         var ret =   {
29399             tag: 'figure',
29400             'data-block' : 'Figure',
29401             'data-width' : this.width, 
29402             contenteditable : 'false',
29403             
29404             style : {
29405                 display: 'block',
29406                 float :  this.align ,
29407                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29408                 width : this.align == 'center' ? '100%' : this.width,
29409                 margin:  '0px',
29410                 padding: this.align == 'center' ? '0' : '0 10px' ,
29411                 textAlign : this.align   // seems to work for email..
29412                 
29413             },
29414            
29415             
29416             align : this.align,
29417             cn : [
29418                 img,
29419               
29420                 {
29421                     tag: 'figcaption',
29422                     'data-display' : this.caption_display,
29423                     style : {
29424                         textAlign : 'left',
29425                         fontSize : '16px',
29426                         lineHeight : '24px',
29427                         display : this.caption_display,
29428                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29429                         margin: m,
29430                         width: this.align == 'center' ?  this.width : '100%' 
29431                     
29432                          
29433                     },
29434                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29435                     cn : [
29436                         {
29437                             tag: 'div',
29438                             style  : {
29439                                 marginTop : '16px',
29440                                 textAlign : 'left'
29441                             },
29442                             align: 'left',
29443                             cn : [
29444                                 {
29445                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29446                                     tag : 'i',
29447                                     contenteditable : true,
29448                                     html : captionhtml
29449                                 }
29450                                 
29451                             ]
29452                         }
29453                         
29454                     ]
29455                     
29456                 }
29457             ]
29458         };
29459         return ret;
29460          
29461     },
29462     
29463     readElement : function(node)
29464     {
29465         // this should not really come from the link...
29466         this.video_url = this.getVal(node, 'div', 'src');
29467         this.cls = this.getVal(node, 'div', 'class');
29468         this.href = this.getVal(node, 'a', 'href');
29469         
29470         
29471         this.image_src = this.getVal(node, 'img', 'src');
29472          
29473         this.align = this.getVal(node, 'figure', 'align');
29474         var figcaption = this.getVal(node, 'figcaption', false);
29475         if (figcaption !== '') {
29476             this.caption = this.getVal(figcaption, 'i', 'html');
29477         }
29478         
29479
29480         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29481         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29482         this.width = this.getVal(node, true, 'data-width');
29483         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29484         
29485     },
29486     removeNode : function()
29487     {
29488         return this.node;
29489     }
29490     
29491   
29492    
29493      
29494     
29495     
29496     
29497     
29498 })
29499
29500  
29501
29502 /**
29503  * @class Roo.htmleditor.BlockTable
29504  * Block that manages a table
29505  * 
29506  * @constructor
29507  * Create a new Filter.
29508  * @param {Object} config Configuration options
29509  */
29510
29511 Roo.htmleditor.BlockTable = function(cfg)
29512 {
29513     if (cfg.node) {
29514         this.readElement(cfg.node);
29515         this.updateElement(cfg.node);
29516     }
29517     Roo.apply(this, cfg);
29518     if (!cfg.node) {
29519         this.rows = [];
29520         for(var r = 0; r < this.no_row; r++) {
29521             this.rows[r] = [];
29522             for(var c = 0; c < this.no_col; c++) {
29523                 this.rows[r][c] = this.emptyCell();
29524             }
29525         }
29526     }
29527     
29528     
29529 }
29530 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29531  
29532     rows : false,
29533     no_col : 1,
29534     no_row : 1,
29535     
29536     
29537     width: '100%',
29538     
29539     // used by context menu
29540     friendly_name : 'Table',
29541     deleteTitle : 'Delete Table',
29542     // context menu is drawn once..
29543     
29544     contextMenu : function(toolbar)
29545     {
29546         
29547         var block = function() {
29548             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29549         };
29550         
29551         
29552         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29553         
29554         var syncValue = toolbar.editorcore.syncValue;
29555         
29556         var fields = {};
29557         
29558         return [
29559             {
29560                 xtype : 'TextItem',
29561                 text : "Width: ",
29562                 xns : rooui.Toolbar  //Boostrap?
29563             },
29564             {
29565                 xtype : 'ComboBox',
29566                 allowBlank : false,
29567                 displayField : 'val',
29568                 editable : true,
29569                 listWidth : 100,
29570                 triggerAction : 'all',
29571                 typeAhead : true,
29572                 valueField : 'val',
29573                 width : 100,
29574                 name : 'width',
29575                 listeners : {
29576                     select : function (combo, r, index)
29577                     {
29578                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29579                         var b = block();
29580                         b.width = r.get('val');
29581                         b.updateElement();
29582                         syncValue();
29583                         toolbar.editorcore.onEditorEvent();
29584                     }
29585                 },
29586                 xns : rooui.form,
29587                 store : {
29588                     xtype : 'SimpleStore',
29589                     data : [
29590                         ['100%'],
29591                         ['auto']
29592                     ],
29593                     fields : [ 'val'],
29594                     xns : Roo.data
29595                 }
29596             },
29597             // -------- Cols
29598             
29599             {
29600                 xtype : 'TextItem',
29601                 text : "Columns: ",
29602                 xns : rooui.Toolbar  //Boostrap?
29603             },
29604          
29605             {
29606                 xtype : 'Button',
29607                 text: '-',
29608                 listeners : {
29609                     click : function (_self, e)
29610                     {
29611                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29612                         block().removeColumn();
29613                         syncValue();
29614                         toolbar.editorcore.onEditorEvent();
29615                     }
29616                 },
29617                 xns : rooui.Toolbar
29618             },
29619             {
29620                 xtype : 'Button',
29621                 text: '+',
29622                 listeners : {
29623                     click : function (_self, e)
29624                     {
29625                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29626                         block().addColumn();
29627                         syncValue();
29628                         toolbar.editorcore.onEditorEvent();
29629                     }
29630                 },
29631                 xns : rooui.Toolbar
29632             },
29633             // -------- ROWS
29634             {
29635                 xtype : 'TextItem',
29636                 text : "Rows: ",
29637                 xns : rooui.Toolbar  //Boostrap?
29638             },
29639          
29640             {
29641                 xtype : 'Button',
29642                 text: '-',
29643                 listeners : {
29644                     click : function (_self, e)
29645                     {
29646                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29647                         block().removeRow();
29648                         syncValue();
29649                         toolbar.editorcore.onEditorEvent();
29650                     }
29651                 },
29652                 xns : rooui.Toolbar
29653             },
29654             {
29655                 xtype : 'Button',
29656                 text: '+',
29657                 listeners : {
29658                     click : function (_self, e)
29659                     {
29660                         block().addRow();
29661                         syncValue();
29662                         toolbar.editorcore.onEditorEvent();
29663                     }
29664                 },
29665                 xns : rooui.Toolbar
29666             },
29667             // -------- ROWS
29668             {
29669                 xtype : 'Button',
29670                 text: 'Reset Column Widths',
29671                 listeners : {
29672                     
29673                     click : function (_self, e)
29674                     {
29675                         block().resetWidths();
29676                         syncValue();
29677                         toolbar.editorcore.onEditorEvent();
29678                     }
29679                 },
29680                 xns : rooui.Toolbar
29681             } 
29682             
29683             
29684             
29685         ];
29686         
29687     },
29688     
29689     
29690   /**
29691      * create a DomHelper friendly object - for use with
29692      * Roo.DomHelper.markup / overwrite / etc..
29693      * ?? should it be called with option to hide all editing features?
29694      */
29695     toObject : function()
29696     {
29697         
29698         var ret = {
29699             tag : 'table',
29700             contenteditable : 'false', // this stops cell selection from picking the table.
29701             'data-block' : 'Table',
29702             style : {
29703                 width:  this.width,
29704                 border : 'solid 1px #000', // ??? hard coded?
29705                 'border-collapse' : 'collapse' 
29706             },
29707             cn : [
29708                 { tag : 'tbody' , cn : [] }
29709             ]
29710         };
29711         
29712         // do we have a head = not really 
29713         var ncols = 0;
29714         Roo.each(this.rows, function( row ) {
29715             var tr = {
29716                 tag: 'tr',
29717                 style : {
29718                     margin: '6px',
29719                     border : 'solid 1px #000',
29720                     textAlign : 'left' 
29721                 },
29722                 cn : [ ]
29723             };
29724             
29725             ret.cn[0].cn.push(tr);
29726             // does the row have any properties? ?? height?
29727             var nc = 0;
29728             Roo.each(row, function( cell ) {
29729                 
29730                 var td = {
29731                     tag : 'td',
29732                     contenteditable :  'true',
29733                     'data-block' : 'Td',
29734                     html : cell.html,
29735                     style : cell.style
29736                 };
29737                 if (cell.colspan > 1) {
29738                     td.colspan = cell.colspan ;
29739                     nc += cell.colspan;
29740                 } else {
29741                     nc++;
29742                 }
29743                 if (cell.rowspan > 1) {
29744                     td.rowspan = cell.rowspan ;
29745                 }
29746                 
29747                 
29748                 // widths ?
29749                 tr.cn.push(td);
29750                     
29751                 
29752             }, this);
29753             ncols = Math.max(nc, ncols);
29754             
29755             
29756         }, this);
29757         // add the header row..
29758         
29759         ncols++;
29760          
29761         
29762         return ret;
29763          
29764     },
29765     
29766     readElement : function(node)
29767     {
29768         node  = node ? node : this.node ;
29769         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29770         
29771         this.rows = [];
29772         this.no_row = 0;
29773         var trs = Array.from(node.rows);
29774         trs.forEach(function(tr) {
29775             var row =  [];
29776             this.rows.push(row);
29777             
29778             this.no_row++;
29779             var no_column = 0;
29780             Array.from(tr.cells).forEach(function(td) {
29781                 
29782                 var add = {
29783                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29784                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29785                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29786                     html : td.innerHTML
29787                 };
29788                 no_column += add.colspan;
29789                      
29790                 
29791                 row.push(add);
29792                 
29793                 
29794             },this);
29795             this.no_col = Math.max(this.no_col, no_column);
29796             
29797             
29798         },this);
29799         
29800         
29801     },
29802     normalizeRows: function()
29803     {
29804         var ret= [];
29805         var rid = -1;
29806         this.rows.forEach(function(row) {
29807             rid++;
29808             ret[rid] = [];
29809             row = this.normalizeRow(row);
29810             var cid = 0;
29811             row.forEach(function(c) {
29812                 while (typeof(ret[rid][cid]) != 'undefined') {
29813                     cid++;
29814                 }
29815                 if (typeof(ret[rid]) == 'undefined') {
29816                     ret[rid] = [];
29817                 }
29818                 ret[rid][cid] = c;
29819                 c.row = rid;
29820                 c.col = cid;
29821                 if (c.rowspan < 2) {
29822                     return;
29823                 }
29824                 
29825                 for(var i = 1 ;i < c.rowspan; i++) {
29826                     if (typeof(ret[rid+i]) == 'undefined') {
29827                         ret[rid+i] = [];
29828                     }
29829                     ret[rid+i][cid] = c;
29830                 }
29831             });
29832         }, this);
29833         return ret;
29834     
29835     },
29836     
29837     normalizeRow: function(row)
29838     {
29839         var ret= [];
29840         row.forEach(function(c) {
29841             if (c.colspan < 2) {
29842                 ret.push(c);
29843                 return;
29844             }
29845             for(var i =0 ;i < c.colspan; i++) {
29846                 ret.push(c);
29847             }
29848         });
29849         return ret;
29850     
29851     },
29852     
29853     deleteColumn : function(sel)
29854     {
29855         if (!sel || sel.type != 'col') {
29856             return;
29857         }
29858         if (this.no_col < 2) {
29859             return;
29860         }
29861         
29862         this.rows.forEach(function(row) {
29863             var cols = this.normalizeRow(row);
29864             var col = cols[sel.col];
29865             if (col.colspan > 1) {
29866                 col.colspan --;
29867             } else {
29868                 row.remove(col);
29869             }
29870             
29871         }, this);
29872         this.no_col--;
29873         
29874     },
29875     removeColumn : function()
29876     {
29877         this.deleteColumn({
29878             type: 'col',
29879             col : this.no_col-1
29880         });
29881         this.updateElement();
29882     },
29883     
29884      
29885     addColumn : function()
29886     {
29887         
29888         this.rows.forEach(function(row) {
29889             row.push(this.emptyCell());
29890            
29891         }, this);
29892         this.updateElement();
29893     },
29894     
29895     deleteRow : function(sel)
29896     {
29897         if (!sel || sel.type != 'row') {
29898             return;
29899         }
29900         
29901         if (this.no_row < 2) {
29902             return;
29903         }
29904         
29905         var rows = this.normalizeRows();
29906         
29907         
29908         rows[sel.row].forEach(function(col) {
29909             if (col.rowspan > 1) {
29910                 col.rowspan--;
29911             } else {
29912                 col.remove = 1; // flage it as removed.
29913             }
29914             
29915         }, this);
29916         var newrows = [];
29917         this.rows.forEach(function(row) {
29918             newrow = [];
29919             row.forEach(function(c) {
29920                 if (typeof(c.remove) == 'undefined') {
29921                     newrow.push(c);
29922                 }
29923                 
29924             });
29925             if (newrow.length > 0) {
29926                 newrows.push(row);
29927             }
29928         });
29929         this.rows =  newrows;
29930         
29931         
29932         
29933         this.no_row--;
29934         this.updateElement();
29935         
29936     },
29937     removeRow : function()
29938     {
29939         this.deleteRow({
29940             type: 'row',
29941             row : this.no_row-1
29942         });
29943         
29944     },
29945     
29946      
29947     addRow : function()
29948     {
29949         
29950         var row = [];
29951         for (var i = 0; i < this.no_col; i++ ) {
29952             
29953             row.push(this.emptyCell());
29954            
29955         }
29956         this.rows.push(row);
29957         this.updateElement();
29958         
29959     },
29960      
29961     // the default cell object... at present...
29962     emptyCell : function() {
29963         return (new Roo.htmleditor.BlockTd({})).toObject();
29964         
29965      
29966     },
29967     
29968     removeNode : function()
29969     {
29970         return this.node;
29971     },
29972     
29973     
29974     
29975     resetWidths : function()
29976     {
29977         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29978             var nn = Roo.htmleditor.Block.factory(n);
29979             nn.width = '';
29980             nn.updateElement(n);
29981         });
29982     }
29983     
29984     
29985     
29986     
29987 })
29988
29989 /**
29990  *
29991  * editing a TD?
29992  *
29993  * since selections really work on the table cell, then editing really should work from there
29994  *
29995  * The original plan was to support merging etc... - but that may not be needed yet..
29996  *
29997  * So this simple version will support:
29998  *   add/remove cols
29999  *   adjust the width +/-
30000  *   reset the width...
30001  *   
30002  *
30003  */
30004
30005
30006  
30007
30008 /**
30009  * @class Roo.htmleditor.BlockTable
30010  * Block that manages a table
30011  * 
30012  * @constructor
30013  * Create a new Filter.
30014  * @param {Object} config Configuration options
30015  */
30016
30017 Roo.htmleditor.BlockTd = function(cfg)
30018 {
30019     if (cfg.node) {
30020         this.readElement(cfg.node);
30021         this.updateElement(cfg.node);
30022     }
30023     Roo.apply(this, cfg);
30024      
30025     
30026     
30027 }
30028 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30029  
30030     node : false,
30031     
30032     width: '',
30033     textAlign : 'left',
30034     valign : 'top',
30035     
30036     colspan : 1,
30037     rowspan : 1,
30038     
30039     
30040     // used by context menu
30041     friendly_name : 'Table Cell',
30042     deleteTitle : false, // use our customer delete
30043     
30044     // context menu is drawn once..
30045     
30046     contextMenu : function(toolbar)
30047     {
30048         
30049         var cell = function() {
30050             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30051         };
30052         
30053         var table = function() {
30054             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30055         };
30056         
30057         var lr = false;
30058         var saveSel = function()
30059         {
30060             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30061         }
30062         var restoreSel = function()
30063         {
30064             if (lr) {
30065                 (function() {
30066                     toolbar.editorcore.focus();
30067                     var cr = toolbar.editorcore.getSelection();
30068                     cr.removeAllRanges();
30069                     cr.addRange(lr);
30070                     toolbar.editorcore.onEditorEvent();
30071                 }).defer(10, this);
30072                 
30073                 
30074             }
30075         }
30076         
30077         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30078         
30079         var syncValue = toolbar.editorcore.syncValue;
30080         
30081         var fields = {};
30082         
30083         return [
30084             {
30085                 xtype : 'Button',
30086                 text : 'Edit Table',
30087                 listeners : {
30088                     click : function() {
30089                         var t = toolbar.tb.selectedNode.closest('table');
30090                         toolbar.editorcore.selectNode(t);
30091                         toolbar.editorcore.onEditorEvent();                        
30092                     }
30093                 }
30094                 
30095             },
30096               
30097            
30098              
30099             {
30100                 xtype : 'TextItem',
30101                 text : "Column Width: ",
30102                  xns : rooui.Toolbar 
30103                
30104             },
30105             {
30106                 xtype : 'Button',
30107                 text: '-',
30108                 listeners : {
30109                     click : function (_self, e)
30110                     {
30111                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30112                         cell().shrinkColumn();
30113                         syncValue();
30114                          toolbar.editorcore.onEditorEvent();
30115                     }
30116                 },
30117                 xns : rooui.Toolbar
30118             },
30119             {
30120                 xtype : 'Button',
30121                 text: '+',
30122                 listeners : {
30123                     click : function (_self, e)
30124                     {
30125                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30126                         cell().growColumn();
30127                         syncValue();
30128                         toolbar.editorcore.onEditorEvent();
30129                     }
30130                 },
30131                 xns : rooui.Toolbar
30132             },
30133             
30134             {
30135                 xtype : 'TextItem',
30136                 text : "Vertical Align: ",
30137                 xns : rooui.Toolbar  //Boostrap?
30138             },
30139             {
30140                 xtype : 'ComboBox',
30141                 allowBlank : false,
30142                 displayField : 'val',
30143                 editable : true,
30144                 listWidth : 100,
30145                 triggerAction : 'all',
30146                 typeAhead : true,
30147                 valueField : 'val',
30148                 width : 100,
30149                 name : 'valign',
30150                 listeners : {
30151                     select : function (combo, r, index)
30152                     {
30153                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30154                         var b = cell();
30155                         b.valign = r.get('val');
30156                         b.updateElement();
30157                         syncValue();
30158                         toolbar.editorcore.onEditorEvent();
30159                     }
30160                 },
30161                 xns : rooui.form,
30162                 store : {
30163                     xtype : 'SimpleStore',
30164                     data : [
30165                         ['top'],
30166                         ['middle'],
30167                         ['bottom'] // there are afew more... 
30168                     ],
30169                     fields : [ 'val'],
30170                     xns : Roo.data
30171                 }
30172             },
30173             
30174             {
30175                 xtype : 'TextItem',
30176                 text : "Merge Cells: ",
30177                  xns : rooui.Toolbar 
30178                
30179             },
30180             
30181             
30182             {
30183                 xtype : 'Button',
30184                 text: 'Right',
30185                 listeners : {
30186                     click : function (_self, e)
30187                     {
30188                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30189                         cell().mergeRight();
30190                         //block().growColumn();
30191                         syncValue();
30192                         toolbar.editorcore.onEditorEvent();
30193                     }
30194                 },
30195                 xns : rooui.Toolbar
30196             },
30197              
30198             {
30199                 xtype : 'Button',
30200                 text: 'Below',
30201                 listeners : {
30202                     click : function (_self, e)
30203                     {
30204                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30205                         cell().mergeBelow();
30206                         //block().growColumn();
30207                         syncValue();
30208                         toolbar.editorcore.onEditorEvent();
30209                     }
30210                 },
30211                 xns : rooui.Toolbar
30212             },
30213             {
30214                 xtype : 'TextItem',
30215                 text : "| ",
30216                  xns : rooui.Toolbar 
30217                
30218             },
30219             
30220             {
30221                 xtype : 'Button',
30222                 text: 'Split',
30223                 listeners : {
30224                     click : function (_self, e)
30225                     {
30226                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30227                         cell().split();
30228                         syncValue();
30229                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30230                         toolbar.editorcore.onEditorEvent();
30231                                              
30232                     }
30233                 },
30234                 xns : rooui.Toolbar
30235             },
30236             {
30237                 xtype : 'Fill',
30238                 xns : rooui.Toolbar 
30239                
30240             },
30241         
30242           
30243             {
30244                 xtype : 'Button',
30245                 text: 'Delete',
30246                  
30247                 xns : rooui.Toolbar,
30248                 menu : {
30249                     xtype : 'Menu',
30250                     xns : rooui.menu,
30251                     items : [
30252                         {
30253                             xtype : 'Item',
30254                             html: 'Column',
30255                             listeners : {
30256                                 click : function (_self, e)
30257                                 {
30258                                     var t = table();
30259                                     
30260                                     cell().deleteColumn();
30261                                     syncValue();
30262                                     toolbar.editorcore.selectNode(t.node);
30263                                     toolbar.editorcore.onEditorEvent();   
30264                                 }
30265                             },
30266                             xns : rooui.menu
30267                         },
30268                         {
30269                             xtype : 'Item',
30270                             html: 'Row',
30271                             listeners : {
30272                                 click : function (_self, e)
30273                                 {
30274                                     var t = table();
30275                                     cell().deleteRow();
30276                                     syncValue();
30277                                     
30278                                     toolbar.editorcore.selectNode(t.node);
30279                                     toolbar.editorcore.onEditorEvent();   
30280                                                          
30281                                 }
30282                             },
30283                             xns : rooui.menu
30284                         },
30285                        {
30286                             xtype : 'Separator',
30287                             xns : rooui.menu
30288                         },
30289                         {
30290                             xtype : 'Item',
30291                             html: 'Table',
30292                             listeners : {
30293                                 click : function (_self, e)
30294                                 {
30295                                     var t = table();
30296                                     var nn = t.node.nextSibling || t.node.previousSibling;
30297                                     t.node.parentNode.removeChild(t.node);
30298                                     if (nn) { 
30299                                         toolbar.editorcore.selectNode(nn, true);
30300                                     }
30301                                     toolbar.editorcore.onEditorEvent();   
30302                                                          
30303                                 }
30304                             },
30305                             xns : rooui.menu
30306                         }
30307                     ]
30308                 }
30309             }
30310             
30311             // align... << fixme
30312             
30313         ];
30314         
30315     },
30316     
30317     
30318   /**
30319      * create a DomHelper friendly object - for use with
30320      * Roo.DomHelper.markup / overwrite / etc..
30321      * ?? should it be called with option to hide all editing features?
30322      */
30323  /**
30324      * create a DomHelper friendly object - for use with
30325      * Roo.DomHelper.markup / overwrite / etc..
30326      * ?? should it be called with option to hide all editing features?
30327      */
30328     toObject : function()
30329     {
30330         var ret = {
30331             tag : 'td',
30332             contenteditable : 'true', // this stops cell selection from picking the table.
30333             'data-block' : 'Td',
30334             valign : this.valign,
30335             style : {  
30336                 'text-align' :  this.textAlign,
30337                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30338                 'border-collapse' : 'collapse',
30339                 padding : '6px', // 8 for desktop / 4 for mobile
30340                 'vertical-align': this.valign
30341             },
30342             html : this.html
30343         };
30344         if (this.width != '') {
30345             ret.width = this.width;
30346             ret.style.width = this.width;
30347         }
30348         
30349         
30350         if (this.colspan > 1) {
30351             ret.colspan = this.colspan ;
30352         } 
30353         if (this.rowspan > 1) {
30354             ret.rowspan = this.rowspan ;
30355         }
30356         
30357            
30358         
30359         return ret;
30360          
30361     },
30362     
30363     readElement : function(node)
30364     {
30365         node  = node ? node : this.node ;
30366         this.width = node.style.width;
30367         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30368         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30369         this.html = node.innerHTML;
30370         if (node.style.textAlign != '') {
30371             this.textAlign = node.style.textAlign;
30372         }
30373         
30374         
30375     },
30376      
30377     // the default cell object... at present...
30378     emptyCell : function() {
30379         return {
30380             colspan :  1,
30381             rowspan :  1,
30382             textAlign : 'left',
30383             html : "&nbsp;" // is this going to be editable now?
30384         };
30385      
30386     },
30387     
30388     removeNode : function()
30389     {
30390         return this.node.closest('table');
30391          
30392     },
30393     
30394     cellData : false,
30395     
30396     colWidths : false,
30397     
30398     toTableArray  : function()
30399     {
30400         var ret = [];
30401         var tab = this.node.closest('tr').closest('table');
30402         Array.from(tab.rows).forEach(function(r, ri){
30403             ret[ri] = [];
30404         });
30405         var rn = 0;
30406         this.colWidths = [];
30407         var all_auto = true;
30408         Array.from(tab.rows).forEach(function(r, ri){
30409             
30410             var cn = 0;
30411             Array.from(r.cells).forEach(function(ce, ci){
30412                 var c =  {
30413                     cell : ce,
30414                     row : rn,
30415                     col: cn,
30416                     colspan : ce.colSpan,
30417                     rowspan : ce.rowSpan
30418                 };
30419                 if (ce.isEqualNode(this.node)) {
30420                     this.cellData = c;
30421                 }
30422                 // if we have been filled up by a row?
30423                 if (typeof(ret[rn][cn]) != 'undefined') {
30424                     while(typeof(ret[rn][cn]) != 'undefined') {
30425                         cn++;
30426                     }
30427                     c.col = cn;
30428                 }
30429                 
30430                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30431                     this.colWidths[cn] =   ce.style.width;
30432                     if (this.colWidths[cn] != '') {
30433                         all_auto = false;
30434                     }
30435                 }
30436                 
30437                 
30438                 if (c.colspan < 2 && c.rowspan < 2 ) {
30439                     ret[rn][cn] = c;
30440                     cn++;
30441                     return;
30442                 }
30443                 for(var j = 0; j < c.rowspan; j++) {
30444                     if (typeof(ret[rn+j]) == 'undefined') {
30445                         continue; // we have a problem..
30446                     }
30447                     ret[rn+j][cn] = c;
30448                     for(var i = 0; i < c.colspan; i++) {
30449                         ret[rn+j][cn+i] = c;
30450                     }
30451                 }
30452                 
30453                 cn += c.colspan;
30454             }, this);
30455             rn++;
30456         }, this);
30457         
30458         // initalize widths.?
30459         // either all widths or no widths..
30460         if (all_auto) {
30461             this.colWidths[0] = false; // no widths flag.
30462         }
30463         
30464         
30465         return ret;
30466         
30467     },
30468     
30469     
30470     
30471     
30472     mergeRight: function()
30473     {
30474          
30475         // get the contents of the next cell along..
30476         var tr = this.node.closest('tr');
30477         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30478         if (i >= tr.childNodes.length - 1) {
30479             return; // no cells on right to merge with.
30480         }
30481         var table = this.toTableArray();
30482         
30483         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30484             return; // nothing right?
30485         }
30486         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30487         // right cell - must be same rowspan and on the same row.
30488         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30489             return; // right hand side is not same rowspan.
30490         }
30491         
30492         
30493         
30494         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30495         tr.removeChild(rc.cell);
30496         this.colspan += rc.colspan;
30497         this.node.setAttribute('colspan', this.colspan);
30498
30499         var table = this.toTableArray();
30500         this.normalizeWidths(table);
30501         this.updateWidths(table);
30502     },
30503     
30504     
30505     mergeBelow : function()
30506     {
30507         var table = this.toTableArray();
30508         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30509             return; // no row below
30510         }
30511         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30512             return; // nothing right?
30513         }
30514         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30515         
30516         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30517             return; // right hand side is not same rowspan.
30518         }
30519         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30520         rc.cell.parentNode.removeChild(rc.cell);
30521         this.rowspan += rc.rowspan;
30522         this.node.setAttribute('rowspan', this.rowspan);
30523     },
30524     
30525     split: function()
30526     {
30527         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30528             return;
30529         }
30530         var table = this.toTableArray();
30531         var cd = this.cellData;
30532         this.rowspan = 1;
30533         this.colspan = 1;
30534         
30535         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30536              
30537             
30538             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30539                 if (r == cd.row && c == cd.col) {
30540                     this.node.removeAttribute('rowspan');
30541                     this.node.removeAttribute('colspan');
30542                 }
30543                  
30544                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30545                 ntd.removeAttribute('id'); 
30546                 ntd.style.width  = this.colWidths[c];
30547                 ntd.innerHTML = '';
30548                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30549             }
30550             
30551         }
30552         this.redrawAllCells(table);
30553         
30554     },
30555     
30556     
30557     
30558     redrawAllCells: function(table)
30559     {
30560         
30561          
30562         var tab = this.node.closest('tr').closest('table');
30563         var ctr = tab.rows[0].parentNode;
30564         Array.from(tab.rows).forEach(function(r, ri){
30565             
30566             Array.from(r.cells).forEach(function(ce, ci){
30567                 ce.parentNode.removeChild(ce);
30568             });
30569             r.parentNode.removeChild(r);
30570         });
30571         for(var r = 0 ; r < table.length; r++) {
30572             var re = tab.rows[r];
30573             
30574             var re = tab.ownerDocument.createElement('tr');
30575             ctr.appendChild(re);
30576             for(var c = 0 ; c < table[r].length; c++) {
30577                 if (table[r][c].cell === false) {
30578                     continue;
30579                 }
30580                 
30581                 re.appendChild(table[r][c].cell);
30582                  
30583                 table[r][c].cell = false;
30584             }
30585         }
30586         
30587     },
30588     updateWidths : function(table)
30589     {
30590         for(var r = 0 ; r < table.length; r++) {
30591            
30592             for(var c = 0 ; c < table[r].length; c++) {
30593                 if (table[r][c].cell === false) {
30594                     continue;
30595                 }
30596                 
30597                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30598                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30599                     el.width = Math.floor(this.colWidths[c])  +'%';
30600                     el.updateElement(el.node);
30601                 }
30602                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30603                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30604                     var width = 0;
30605                     for(var i = 0; i < table[r][c].colspan; i ++) {
30606                         width += Math.floor(this.colWidths[c + i]);
30607                     }
30608                     el.width = width  +'%';
30609                     el.updateElement(el.node);
30610                 }
30611                 table[r][c].cell = false; // done
30612             }
30613         }
30614     },
30615     normalizeWidths : function(table)
30616     {
30617         if (this.colWidths[0] === false) {
30618             var nw = 100.0 / this.colWidths.length;
30619             this.colWidths.forEach(function(w,i) {
30620                 this.colWidths[i] = nw;
30621             },this);
30622             return;
30623         }
30624     
30625         var t = 0, missing = [];
30626         
30627         this.colWidths.forEach(function(w,i) {
30628             //if you mix % and
30629             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30630             var add =  this.colWidths[i];
30631             if (add > 0) {
30632                 t+=add;
30633                 return;
30634             }
30635             missing.push(i);
30636             
30637             
30638         },this);
30639         var nc = this.colWidths.length;
30640         if (missing.length) {
30641             var mult = (nc - missing.length) / (1.0 * nc);
30642             var t = mult * t;
30643             var ew = (100 -t) / (1.0 * missing.length);
30644             this.colWidths.forEach(function(w,i) {
30645                 if (w > 0) {
30646                     this.colWidths[i] = w * mult;
30647                     return;
30648                 }
30649                 
30650                 this.colWidths[i] = ew;
30651             }, this);
30652             // have to make up numbers..
30653              
30654         }
30655         // now we should have all the widths..
30656         
30657     
30658     },
30659     
30660     shrinkColumn : function()
30661     {
30662         var table = this.toTableArray();
30663         this.normalizeWidths(table);
30664         var col = this.cellData.col;
30665         var nw = this.colWidths[col] * 0.8;
30666         if (nw < 5) {
30667             return;
30668         }
30669         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30670         this.colWidths.forEach(function(w,i) {
30671             if (i == col) {
30672                  this.colWidths[i] = nw;
30673                 return;
30674             }
30675             this.colWidths[i] += otherAdd
30676         }, this);
30677         this.updateWidths(table);
30678          
30679     },
30680     growColumn : function()
30681     {
30682         var table = this.toTableArray();
30683         this.normalizeWidths(table);
30684         var col = this.cellData.col;
30685         var nw = this.colWidths[col] * 1.2;
30686         if (nw > 90) {
30687             return;
30688         }
30689         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30690         this.colWidths.forEach(function(w,i) {
30691             if (i == col) {
30692                 this.colWidths[i] = nw;
30693                 return;
30694             }
30695             this.colWidths[i] -= otherSub
30696         }, this);
30697         this.updateWidths(table);
30698          
30699     },
30700     deleteRow : function()
30701     {
30702         // delete this rows 'tr'
30703         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30704         // then reduce the rowspan.
30705         var table = this.toTableArray();
30706         // this.cellData.row;
30707         for (var i =0;i< table[this.cellData.row].length ; i++) {
30708             var c = table[this.cellData.row][i];
30709             if (c.row != this.cellData.row) {
30710                 
30711                 c.rowspan--;
30712                 c.cell.setAttribute('rowspan', c.rowspan);
30713                 continue;
30714             }
30715             if (c.rowspan > 1) {
30716                 c.rowspan--;
30717                 c.cell.setAttribute('rowspan', c.rowspan);
30718             }
30719         }
30720         table.splice(this.cellData.row,1);
30721         this.redrawAllCells(table);
30722         
30723     },
30724     deleteColumn : function()
30725     {
30726         var table = this.toTableArray();
30727         
30728         for (var i =0;i< table.length ; i++) {
30729             var c = table[i][this.cellData.col];
30730             if (c.col != this.cellData.col) {
30731                 table[i][this.cellData.col].colspan--;
30732             } else if (c.colspan > 1) {
30733                 c.colspan--;
30734                 c.cell.setAttribute('colspan', c.colspan);
30735             }
30736             table[i].splice(this.cellData.col,1);
30737         }
30738         
30739         this.redrawAllCells(table);
30740     }
30741     
30742     
30743     
30744     
30745 })
30746
30747 //<script type="text/javascript">
30748
30749 /*
30750  * Based  Ext JS Library 1.1.1
30751  * Copyright(c) 2006-2007, Ext JS, LLC.
30752  * LGPL
30753  *
30754  */
30755  
30756 /**
30757  * @class Roo.HtmlEditorCore
30758  * @extends Roo.Component
30759  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30760  *
30761  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30762  */
30763
30764 Roo.HtmlEditorCore = function(config){
30765     
30766     
30767     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30768     
30769     
30770     this.addEvents({
30771         /**
30772          * @event initialize
30773          * Fires when the editor is fully initialized (including the iframe)
30774          * @param {Roo.HtmlEditorCore} this
30775          */
30776         initialize: true,
30777         /**
30778          * @event activate
30779          * Fires when the editor is first receives the focus. Any insertion must wait
30780          * until after this event.
30781          * @param {Roo.HtmlEditorCore} this
30782          */
30783         activate: true,
30784          /**
30785          * @event beforesync
30786          * Fires before the textarea is updated with content from the editor iframe. Return false
30787          * to cancel the sync.
30788          * @param {Roo.HtmlEditorCore} this
30789          * @param {String} html
30790          */
30791         beforesync: true,
30792          /**
30793          * @event beforepush
30794          * Fires before the iframe editor is updated with content from the textarea. Return false
30795          * to cancel the push.
30796          * @param {Roo.HtmlEditorCore} this
30797          * @param {String} html
30798          */
30799         beforepush: true,
30800          /**
30801          * @event sync
30802          * Fires when the textarea is updated with content from the editor iframe.
30803          * @param {Roo.HtmlEditorCore} this
30804          * @param {String} html
30805          */
30806         sync: true,
30807          /**
30808          * @event push
30809          * Fires when the iframe editor is updated with content from the textarea.
30810          * @param {Roo.HtmlEditorCore} this
30811          * @param {String} html
30812          */
30813         push: true,
30814         
30815         /**
30816          * @event editorevent
30817          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30818          * @param {Roo.HtmlEditorCore} this
30819          */
30820         editorevent: true 
30821          
30822         
30823     });
30824     
30825     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30826     
30827     // defaults : white / black...
30828     this.applyBlacklists();
30829     
30830     
30831     
30832 };
30833
30834
30835 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30836
30837
30838      /**
30839      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30840      */
30841     
30842     owner : false,
30843     
30844      /**
30845      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30846      *                        Roo.resizable.
30847      */
30848     resizable : false,
30849      /**
30850      * @cfg {Number} height (in pixels)
30851      */   
30852     height: 300,
30853    /**
30854      * @cfg {Number} width (in pixels)
30855      */   
30856     width: 500,
30857      /**
30858      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30859      *         if you are doing an email editor, this probably needs disabling, it's designed
30860      */
30861     autoClean: true,
30862     
30863     /**
30864      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30865      */
30866     enableBlocks : true,
30867     /**
30868      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30869      * 
30870      */
30871     stylesheets: false,
30872      /**
30873      * @cfg {String} language default en - language of text (usefull for rtl languages)
30874      * 
30875      */
30876     language: 'en',
30877     
30878     /**
30879      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30880      *          - by default they are stripped - if you are editing email you may need this.
30881      */
30882     allowComments: false,
30883     // id of frame..
30884     frameId: false,
30885     
30886     // private properties
30887     validationEvent : false,
30888     deferHeight: true,
30889     initialized : false,
30890     activated : false,
30891     sourceEditMode : false,
30892     onFocus : Roo.emptyFn,
30893     iframePad:3,
30894     hideMode:'offsets',
30895     
30896     clearUp: true,
30897     
30898     // blacklist + whitelisted elements..
30899     black: false,
30900     white: false,
30901      
30902     bodyCls : '',
30903
30904     
30905     undoManager : false,
30906     /**
30907      * Protected method that will not generally be called directly. It
30908      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30909      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30910      */
30911     getDocMarkup : function(){
30912         // body styles..
30913         var st = '';
30914         
30915         // inherit styels from page...?? 
30916         if (this.stylesheets === false) {
30917             
30918             Roo.get(document.head).select('style').each(function(node) {
30919                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30920             });
30921             
30922             Roo.get(document.head).select('link').each(function(node) { 
30923                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30924             });
30925             
30926         } else if (!this.stylesheets.length) {
30927                 // simple..
30928                 st = '<style type="text/css">' +
30929                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30930                    '</style>';
30931         } else {
30932             for (var i in this.stylesheets) {
30933                 if (typeof(this.stylesheets[i]) != 'string') {
30934                     continue;
30935                 }
30936                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30937             }
30938             
30939         }
30940         
30941         st +=  '<style type="text/css">' +
30942             'IMG { cursor: pointer } ' +
30943         '</style>';
30944         
30945         st += '<meta name="google" content="notranslate">';
30946         
30947         var cls = 'notranslate roo-htmleditor-body';
30948         
30949         if(this.bodyCls.length){
30950             cls += ' ' + this.bodyCls;
30951         }
30952         
30953         return '<html  class="notranslate" translate="no"><head>' + st  +
30954             //<style type="text/css">' +
30955             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30956             //'</style>' +
30957             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30958     },
30959
30960     // private
30961     onRender : function(ct, position)
30962     {
30963         var _t = this;
30964         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30965         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30966         
30967         
30968         this.el.dom.style.border = '0 none';
30969         this.el.dom.setAttribute('tabIndex', -1);
30970         this.el.addClass('x-hidden hide');
30971         
30972         
30973         
30974         if(Roo.isIE){ // fix IE 1px bogus margin
30975             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30976         }
30977        
30978         
30979         this.frameId = Roo.id();
30980         
30981          
30982         
30983         var iframe = this.owner.wrap.createChild({
30984             tag: 'iframe',
30985             cls: 'form-control', // bootstrap..
30986             id: this.frameId,
30987             name: this.frameId,
30988             frameBorder : 'no',
30989             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30990         }, this.el
30991         );
30992         
30993         
30994         this.iframe = iframe.dom;
30995
30996         this.assignDocWin();
30997         
30998         this.doc.designMode = 'on';
30999        
31000         this.doc.open();
31001         this.doc.write(this.getDocMarkup());
31002         this.doc.close();
31003
31004         
31005         var task = { // must defer to wait for browser to be ready
31006             run : function(){
31007                 //console.log("run task?" + this.doc.readyState);
31008                 this.assignDocWin();
31009                 if(this.doc.body || this.doc.readyState == 'complete'){
31010                     try {
31011                         this.doc.designMode="on";
31012                         
31013                     } catch (e) {
31014                         return;
31015                     }
31016                     Roo.TaskMgr.stop(task);
31017                     this.initEditor.defer(10, this);
31018                 }
31019             },
31020             interval : 10,
31021             duration: 10000,
31022             scope: this
31023         };
31024         Roo.TaskMgr.start(task);
31025
31026     },
31027
31028     // private
31029     onResize : function(w, h)
31030     {
31031          Roo.log('resize: ' +w + ',' + h );
31032         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31033         if(!this.iframe){
31034             return;
31035         }
31036         if(typeof w == 'number'){
31037             
31038             this.iframe.style.width = w + 'px';
31039         }
31040         if(typeof h == 'number'){
31041             
31042             this.iframe.style.height = h + 'px';
31043             if(this.doc){
31044                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31045             }
31046         }
31047         
31048     },
31049
31050     /**
31051      * Toggles the editor between standard and source edit mode.
31052      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31053      */
31054     toggleSourceEdit : function(sourceEditMode){
31055         
31056         this.sourceEditMode = sourceEditMode === true;
31057         
31058         if(this.sourceEditMode){
31059  
31060             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31061             
31062         }else{
31063             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31064             //this.iframe.className = '';
31065             this.deferFocus();
31066         }
31067         //this.setSize(this.owner.wrap.getSize());
31068         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31069     },
31070
31071     
31072   
31073
31074     /**
31075      * Protected method that will not generally be called directly. If you need/want
31076      * custom HTML cleanup, this is the method you should override.
31077      * @param {String} html The HTML to be cleaned
31078      * return {String} The cleaned HTML
31079      */
31080     cleanHtml : function(html)
31081     {
31082         html = String(html);
31083         if(html.length > 5){
31084             if(Roo.isSafari){ // strip safari nonsense
31085                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31086             }
31087         }
31088         if(html == '&nbsp;'){
31089             html = '';
31090         }
31091         return html;
31092     },
31093
31094     /**
31095      * HTML Editor -> Textarea
31096      * Protected method that will not generally be called directly. Syncs the contents
31097      * of the editor iframe with the textarea.
31098      */
31099     syncValue : function()
31100     {
31101         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31102         if(this.initialized){
31103             
31104             if (this.undoManager) {
31105                 this.undoManager.addEvent();
31106             }
31107
31108             
31109             var bd = (this.doc.body || this.doc.documentElement);
31110            
31111             
31112             var sel = this.win.getSelection();
31113             
31114             var div = document.createElement('div');
31115             div.innerHTML = bd.innerHTML;
31116             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31117             if (gtx.length > 0) {
31118                 var rm = gtx.item(0).parentNode;
31119                 rm.parentNode.removeChild(rm);
31120             }
31121             
31122            
31123             if (this.enableBlocks) {
31124                 new Roo.htmleditor.FilterBlock({ node : div });
31125             }
31126             
31127             var html = div.innerHTML;
31128             
31129             //?? tidy?
31130             if (this.autoClean) {
31131                 
31132                 new Roo.htmleditor.FilterAttributes({
31133                     node : div,
31134                     attrib_white : [
31135                             'href',
31136                             'src',
31137                             'name',
31138                             'align',
31139                             'colspan',
31140                             'rowspan',
31141                             'data-display',
31142                             'data-width',
31143                             'start' ,
31144                             'style',
31145                             // youtube embed.
31146                             'class',
31147                             'allowfullscreen',
31148                             'frameborder',
31149                             'width',
31150                             'height',
31151                             'alt'
31152                             ],
31153                     attrib_clean : ['href', 'src' ] 
31154                 });
31155                 
31156                 var tidy = new Roo.htmleditor.TidySerializer({
31157                     inner:  true
31158                 });
31159                 html  = tidy.serialize(div);
31160                 
31161             }
31162             
31163             
31164             if(Roo.isSafari){
31165                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31166                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31167                 if(m && m[1]){
31168                     html = '<div style="'+m[0]+'">' + html + '</div>';
31169                 }
31170             }
31171             html = this.cleanHtml(html);
31172             // fix up the special chars.. normaly like back quotes in word...
31173             // however we do not want to do this with chinese..
31174             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31175                 
31176                 var cc = match.charCodeAt();
31177
31178                 // Get the character value, handling surrogate pairs
31179                 if (match.length == 2) {
31180                     // It's a surrogate pair, calculate the Unicode code point
31181                     var high = match.charCodeAt(0) - 0xD800;
31182                     var low  = match.charCodeAt(1) - 0xDC00;
31183                     cc = (high * 0x400) + low + 0x10000;
31184                 }  else if (
31185                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31186                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31187                     (cc >= 0xf900 && cc < 0xfb00 )
31188                 ) {
31189                         return match;
31190                 }  
31191          
31192                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31193                 return "&#" + cc + ";";
31194                 
31195                 
31196             });
31197             
31198             
31199              
31200             if(this.owner.fireEvent('beforesync', this, html) !== false){
31201                 this.el.dom.value = html;
31202                 this.owner.fireEvent('sync', this, html);
31203             }
31204         }
31205     },
31206
31207     /**
31208      * TEXTAREA -> EDITABLE
31209      * Protected method that will not generally be called directly. Pushes the value of the textarea
31210      * into the iframe editor.
31211      */
31212     pushValue : function()
31213     {
31214         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31215         if(this.initialized){
31216             var v = this.el.dom.value.trim();
31217             
31218             
31219             if(this.owner.fireEvent('beforepush', this, v) !== false){
31220                 var d = (this.doc.body || this.doc.documentElement);
31221                 d.innerHTML = v;
31222                  
31223                 this.el.dom.value = d.innerHTML;
31224                 this.owner.fireEvent('push', this, v);
31225             }
31226             if (this.autoClean) {
31227                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31228                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31229             }
31230             if (this.enableBlocks) {
31231                 Roo.htmleditor.Block.initAll(this.doc.body);
31232             }
31233             
31234             this.updateLanguage();
31235             
31236             var lc = this.doc.body.lastChild;
31237             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31238                 // add an extra line at the end.
31239                 this.doc.body.appendChild(this.doc.createElement('br'));
31240             }
31241             
31242             
31243         }
31244     },
31245
31246     // private
31247     deferFocus : function(){
31248         this.focus.defer(10, this);
31249     },
31250
31251     // doc'ed in Field
31252     focus : function(){
31253         if(this.win && !this.sourceEditMode){
31254             this.win.focus();
31255         }else{
31256             this.el.focus();
31257         }
31258     },
31259     
31260     assignDocWin: function()
31261     {
31262         var iframe = this.iframe;
31263         
31264          if(Roo.isIE){
31265             this.doc = iframe.contentWindow.document;
31266             this.win = iframe.contentWindow;
31267         } else {
31268 //            if (!Roo.get(this.frameId)) {
31269 //                return;
31270 //            }
31271 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31272 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31273             
31274             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31275                 return;
31276             }
31277             
31278             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31279             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31280         }
31281     },
31282     
31283     // private
31284     initEditor : function(){
31285         //console.log("INIT EDITOR");
31286         this.assignDocWin();
31287         
31288         
31289         
31290         this.doc.designMode="on";
31291         this.doc.open();
31292         this.doc.write(this.getDocMarkup());
31293         this.doc.close();
31294         
31295         var dbody = (this.doc.body || this.doc.documentElement);
31296         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31297         // this copies styles from the containing element into thsi one..
31298         // not sure why we need all of this..
31299         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31300         
31301         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31302         //ss['background-attachment'] = 'fixed'; // w3c
31303         dbody.bgProperties = 'fixed'; // ie
31304         dbody.setAttribute("translate", "no");
31305         
31306         //Roo.DomHelper.applyStyles(dbody, ss);
31307         Roo.EventManager.on(this.doc, {
31308              
31309             'mouseup': this.onEditorEvent,
31310             'dblclick': this.onEditorEvent,
31311             'click': this.onEditorEvent,
31312             'keyup': this.onEditorEvent,
31313             
31314             buffer:100,
31315             scope: this
31316         });
31317         Roo.EventManager.on(this.doc, {
31318             'paste': this.onPasteEvent,
31319             scope : this
31320         });
31321         if(Roo.isGecko){
31322             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31323         }
31324         //??? needed???
31325         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31326             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31327         }
31328         this.initialized = true;
31329
31330         
31331         // initialize special key events - enter
31332         new Roo.htmleditor.KeyEnter({core : this});
31333         
31334          
31335         
31336         this.owner.fireEvent('initialize', this);
31337         this.pushValue();
31338     },
31339     // this is to prevent a href clicks resulting in a redirect?
31340    
31341     onPasteEvent : function(e,v)
31342     {
31343         // I think we better assume paste is going to be a dirty load of rubish from word..
31344         
31345         // even pasting into a 'email version' of this widget will have to clean up that mess.
31346         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31347         
31348         // check what type of paste - if it's an image, then handle it differently.
31349         if (cd.files && cd.files.length > 0) {
31350             // pasting images?
31351             var urlAPI = (window.createObjectURL && window) || 
31352                 (window.URL && URL.revokeObjectURL && URL) || 
31353                 (window.webkitURL && webkitURL);
31354     
31355             var url = urlAPI.createObjectURL( cd.files[0]);
31356             this.insertAtCursor('<img src=" + url + ">');
31357             return false;
31358         }
31359         if (cd.types.indexOf('text/html') < 0 ) {
31360             return false;
31361         }
31362         var images = [];
31363         var html = cd.getData('text/html'); // clipboard event
31364         if (cd.types.indexOf('text/rtf') > -1) {
31365             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31366             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31367         }
31368         //Roo.log(images);
31369         //Roo.log(imgs);
31370         // fixme..
31371         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31372                        .map(function(g) { return g.toDataURL(); })
31373                        .filter(function(g) { return g != 'about:blank'; });
31374         
31375         //Roo.log(html);
31376         html = this.cleanWordChars(html);
31377         
31378         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31379         
31380         
31381         var sn = this.getParentElement();
31382         // check if d contains a table, and prevent nesting??
31383         //Roo.log(d.getElementsByTagName('table'));
31384         //Roo.log(sn);
31385         //Roo.log(sn.closest('table'));
31386         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31387             e.preventDefault();
31388             this.insertAtCursor("You can not nest tables");
31389             //Roo.log("prevent?"); // fixme - 
31390             return false;
31391         }
31392         
31393         
31394         
31395         if (images.length > 0) {
31396             // replace all v:imagedata - with img.
31397             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31398             Roo.each(ar, function(node) {
31399                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31400                 node.parentNode.removeChild(node);
31401             });
31402             
31403             
31404             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31405                 img.setAttribute('src', images[i]);
31406             });
31407         }
31408         if (this.autoClean) {
31409             new Roo.htmleditor.FilterWord({ node : d });
31410             
31411             new Roo.htmleditor.FilterStyleToTag({ node : d });
31412             new Roo.htmleditor.FilterAttributes({
31413                 node : d,
31414                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31415                 attrib_clean : ['href', 'src' ] 
31416             });
31417             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31418             // should be fonts..
31419             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31420             new Roo.htmleditor.FilterParagraph({ node : d });
31421             new Roo.htmleditor.FilterSpan({ node : d });
31422             new Roo.htmleditor.FilterLongBr({ node : d });
31423             new Roo.htmleditor.FilterComment({ node : d });
31424             
31425             
31426         }
31427         if (this.enableBlocks) {
31428                 
31429             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31430                 if (img.closest('figure')) { // assume!! that it's aready
31431                     return;
31432                 }
31433                 var fig  = new Roo.htmleditor.BlockFigure({
31434                     image_src  : img.src
31435                 });
31436                 fig.updateElement(img); // replace it..
31437                 
31438             });
31439         }
31440         
31441         
31442         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31443         if (this.enableBlocks) {
31444             Roo.htmleditor.Block.initAll(this.doc.body);
31445         }
31446          
31447         
31448         e.preventDefault();
31449         this.owner.fireEvent('paste', this);
31450         return false;
31451         // default behaveiour should be our local cleanup paste? (optional?)
31452         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31453         //this.owner.fireEvent('paste', e, v);
31454     },
31455     // private
31456     onDestroy : function(){
31457         
31458         
31459         
31460         if(this.rendered){
31461             
31462             //for (var i =0; i < this.toolbars.length;i++) {
31463             //    // fixme - ask toolbars for heights?
31464             //    this.toolbars[i].onDestroy();
31465            // }
31466             
31467             //this.wrap.dom.innerHTML = '';
31468             //this.wrap.remove();
31469         }
31470     },
31471
31472     // private
31473     onFirstFocus : function(){
31474         
31475         this.assignDocWin();
31476         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31477         
31478         this.activated = true;
31479          
31480     
31481         if(Roo.isGecko){ // prevent silly gecko errors
31482             this.win.focus();
31483             var s = this.win.getSelection();
31484             if(!s.focusNode || s.focusNode.nodeType != 3){
31485                 var r = s.getRangeAt(0);
31486                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31487                 r.collapse(true);
31488                 this.deferFocus();
31489             }
31490             try{
31491                 this.execCmd('useCSS', true);
31492                 this.execCmd('styleWithCSS', false);
31493             }catch(e){}
31494         }
31495         this.owner.fireEvent('activate', this);
31496     },
31497
31498     // private
31499     adjustFont: function(btn){
31500         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31501         //if(Roo.isSafari){ // safari
31502         //    adjust *= 2;
31503        // }
31504         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31505         if(Roo.isSafari){ // safari
31506             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31507             v =  (v < 10) ? 10 : v;
31508             v =  (v > 48) ? 48 : v;
31509             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31510             
31511         }
31512         
31513         
31514         v = Math.max(1, v+adjust);
31515         
31516         this.execCmd('FontSize', v  );
31517     },
31518
31519     onEditorEvent : function(e)
31520     {
31521          
31522         
31523         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31524             return; // we do not handle this.. (undo manager does..)
31525         }
31526         // in theory this detects if the last element is not a br, then we try and do that.
31527         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31528         if (e &&
31529             e.target.nodeName == 'BODY' &&
31530             e.type == "mouseup" &&
31531             this.doc.body.lastChild
31532            ) {
31533             var lc = this.doc.body.lastChild;
31534             // gtx-trans is google translate plugin adding crap.
31535             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31536                 lc = lc.previousSibling;
31537             }
31538             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31539             // if last element is <BR> - then dont do anything.
31540             
31541                 var ns = this.doc.createElement('br');
31542                 this.doc.body.appendChild(ns);
31543                 range = this.doc.createRange();
31544                 range.setStartAfter(ns);
31545                 range.collapse(true);
31546                 var sel = this.win.getSelection();
31547                 sel.removeAllRanges();
31548                 sel.addRange(range);
31549             }
31550         }
31551         
31552         
31553         
31554         this.fireEditorEvent(e);
31555       //  this.updateToolbar();
31556         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31557     },
31558     
31559     fireEditorEvent: function(e)
31560     {
31561         this.owner.fireEvent('editorevent', this, e);
31562     },
31563
31564     insertTag : function(tg)
31565     {
31566         // could be a bit smarter... -> wrap the current selected tRoo..
31567         if (tg.toLowerCase() == 'span' ||
31568             tg.toLowerCase() == 'code' ||
31569             tg.toLowerCase() == 'sup' ||
31570             tg.toLowerCase() == 'sub' 
31571             ) {
31572             
31573             range = this.createRange(this.getSelection());
31574             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31575             wrappingNode.appendChild(range.extractContents());
31576             range.insertNode(wrappingNode);
31577
31578             return;
31579             
31580             
31581             
31582         }
31583         this.execCmd("formatblock",   tg);
31584         this.undoManager.addEvent(); 
31585     },
31586     
31587     insertText : function(txt)
31588     {
31589         
31590         
31591         var range = this.createRange();
31592         range.deleteContents();
31593                //alert(Sender.getAttribute('label'));
31594                
31595         range.insertNode(this.doc.createTextNode(txt));
31596         this.undoManager.addEvent();
31597     } ,
31598     
31599      
31600
31601     /**
31602      * Executes a Midas editor command on the editor document and performs necessary focus and
31603      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31604      * @param {String} cmd The Midas command
31605      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31606      */
31607     relayCmd : function(cmd, value)
31608     {
31609         
31610         switch (cmd) {
31611             case 'justifyleft':
31612             case 'justifyright':
31613             case 'justifycenter':
31614                 // if we are in a cell, then we will adjust the
31615                 var n = this.getParentElement();
31616                 var td = n.closest('td');
31617                 if (td) {
31618                     var bl = Roo.htmleditor.Block.factory(td);
31619                     bl.textAlign = cmd.replace('justify','');
31620                     bl.updateElement();
31621                     this.owner.fireEvent('editorevent', this);
31622                     return;
31623                 }
31624                 this.execCmd('styleWithCSS', true); // 
31625                 break;
31626             case 'bold':
31627             case 'italic':
31628                 // if there is no selection, then we insert, and set the curson inside it..
31629                 this.execCmd('styleWithCSS', false); 
31630                 break;
31631                 
31632         
31633             default:
31634                 break;
31635         }
31636         
31637         
31638         this.win.focus();
31639         this.execCmd(cmd, value);
31640         this.owner.fireEvent('editorevent', this);
31641         //this.updateToolbar();
31642         this.owner.deferFocus();
31643     },
31644
31645     /**
31646      * Executes a Midas editor command directly on the editor document.
31647      * For visual commands, you should use {@link #relayCmd} instead.
31648      * <b>This should only be called after the editor is initialized.</b>
31649      * @param {String} cmd The Midas command
31650      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31651      */
31652     execCmd : function(cmd, value){
31653         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31654         this.syncValue();
31655     },
31656  
31657  
31658    
31659     /**
31660      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31661      * to insert tRoo.
31662      * @param {String} text | dom node.. 
31663      */
31664     insertAtCursor : function(text)
31665     {
31666         
31667         if(!this.activated){
31668             return;
31669         }
31670          
31671         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31672             this.win.focus();
31673             
31674             
31675             // from jquery ui (MIT licenced)
31676             var range, node;
31677             var win = this.win;
31678             
31679             if (win.getSelection && win.getSelection().getRangeAt) {
31680                 
31681                 // delete the existing?
31682                 
31683                 this.createRange(this.getSelection()).deleteContents();
31684                 range = win.getSelection().getRangeAt(0);
31685                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31686                 range.insertNode(node);
31687                 range = range.cloneRange();
31688                 range.collapse(false);
31689                  
31690                 win.getSelection().removeAllRanges();
31691                 win.getSelection().addRange(range);
31692                 
31693                 
31694                 
31695             } else if (win.document.selection && win.document.selection.createRange) {
31696                 // no firefox support
31697                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31698                 win.document.selection.createRange().pasteHTML(txt);
31699             
31700             } else {
31701                 // no firefox support
31702                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31703                 this.execCmd('InsertHTML', txt);
31704             } 
31705             this.syncValue();
31706             
31707             this.deferFocus();
31708         }
31709     },
31710  // private
31711     mozKeyPress : function(e){
31712         if(e.ctrlKey){
31713             var c = e.getCharCode(), cmd;
31714           
31715             if(c > 0){
31716                 c = String.fromCharCode(c).toLowerCase();
31717                 switch(c){
31718                     case 'b':
31719                         cmd = 'bold';
31720                         break;
31721                     case 'i':
31722                         cmd = 'italic';
31723                         break;
31724                     
31725                     case 'u':
31726                         cmd = 'underline';
31727                         break;
31728                     
31729                     //case 'v':
31730                       //  this.cleanUpPaste.defer(100, this);
31731                       //  return;
31732                         
31733                 }
31734                 if(cmd){
31735                     
31736                     this.relayCmd(cmd);
31737                     //this.win.focus();
31738                     //this.execCmd(cmd);
31739                     //this.deferFocus();
31740                     e.preventDefault();
31741                 }
31742                 
31743             }
31744         }
31745     },
31746
31747     // private
31748     fixKeys : function(){ // load time branching for fastest keydown performance
31749         
31750         
31751         if(Roo.isIE){
31752             return function(e){
31753                 var k = e.getKey(), r;
31754                 if(k == e.TAB){
31755                     e.stopEvent();
31756                     r = this.doc.selection.createRange();
31757                     if(r){
31758                         r.collapse(true);
31759                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31760                         this.deferFocus();
31761                     }
31762                     return;
31763                 }
31764                 /// this is handled by Roo.htmleditor.KeyEnter
31765                  /*
31766                 if(k == e.ENTER){
31767                     r = this.doc.selection.createRange();
31768                     if(r){
31769                         var target = r.parentElement();
31770                         if(!target || target.tagName.toLowerCase() != 'li'){
31771                             e.stopEvent();
31772                             r.pasteHTML('<br/>');
31773                             r.collapse(false);
31774                             r.select();
31775                         }
31776                     }
31777                 }
31778                 */
31779                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31780                 //    this.cleanUpPaste.defer(100, this);
31781                 //    return;
31782                 //}
31783                 
31784                 
31785             };
31786         }else if(Roo.isOpera){
31787             return function(e){
31788                 var k = e.getKey();
31789                 if(k == e.TAB){
31790                     e.stopEvent();
31791                     this.win.focus();
31792                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31793                     this.deferFocus();
31794                 }
31795                
31796                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31797                 //    this.cleanUpPaste.defer(100, this);
31798                  //   return;
31799                 //}
31800                 
31801             };
31802         }else if(Roo.isSafari){
31803             return function(e){
31804                 var k = e.getKey();
31805                 
31806                 if(k == e.TAB){
31807                     e.stopEvent();
31808                     this.execCmd('InsertText','\t');
31809                     this.deferFocus();
31810                     return;
31811                 }
31812                  this.mozKeyPress(e);
31813                 
31814                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31815                  //   this.cleanUpPaste.defer(100, this);
31816                  //   return;
31817                // }
31818                 
31819              };
31820         }
31821     }(),
31822     
31823     getAllAncestors: function()
31824     {
31825         var p = this.getSelectedNode();
31826         var a = [];
31827         if (!p) {
31828             a.push(p); // push blank onto stack..
31829             p = this.getParentElement();
31830         }
31831         
31832         
31833         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31834             a.push(p);
31835             p = p.parentNode;
31836         }
31837         a.push(this.doc.body);
31838         return a;
31839     },
31840     lastSel : false,
31841     lastSelNode : false,
31842     
31843     
31844     getSelection : function() 
31845     {
31846         this.assignDocWin();
31847         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31848     },
31849     /**
31850      * Select a dom node
31851      * @param {DomElement} node the node to select
31852      */
31853     selectNode : function(node, collapse)
31854     {
31855         var nodeRange = node.ownerDocument.createRange();
31856         try {
31857             nodeRange.selectNode(node);
31858         } catch (e) {
31859             nodeRange.selectNodeContents(node);
31860         }
31861         if (collapse === true) {
31862             nodeRange.collapse(true);
31863         }
31864         //
31865         var s = this.win.getSelection();
31866         s.removeAllRanges();
31867         s.addRange(nodeRange);
31868     },
31869     
31870     getSelectedNode: function() 
31871     {
31872         // this may only work on Gecko!!!
31873         
31874         // should we cache this!!!!
31875         
31876          
31877          
31878         var range = this.createRange(this.getSelection()).cloneRange();
31879         
31880         if (Roo.isIE) {
31881             var parent = range.parentElement();
31882             while (true) {
31883                 var testRange = range.duplicate();
31884                 testRange.moveToElementText(parent);
31885                 if (testRange.inRange(range)) {
31886                     break;
31887                 }
31888                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31889                     break;
31890                 }
31891                 parent = parent.parentElement;
31892             }
31893             return parent;
31894         }
31895         
31896         // is ancestor a text element.
31897         var ac =  range.commonAncestorContainer;
31898         if (ac.nodeType == 3) {
31899             ac = ac.parentNode;
31900         }
31901         
31902         var ar = ac.childNodes;
31903          
31904         var nodes = [];
31905         var other_nodes = [];
31906         var has_other_nodes = false;
31907         for (var i=0;i<ar.length;i++) {
31908             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31909                 continue;
31910             }
31911             // fullly contained node.
31912             
31913             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31914                 nodes.push(ar[i]);
31915                 continue;
31916             }
31917             
31918             // probably selected..
31919             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31920                 other_nodes.push(ar[i]);
31921                 continue;
31922             }
31923             // outer..
31924             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31925                 continue;
31926             }
31927             
31928             
31929             has_other_nodes = true;
31930         }
31931         if (!nodes.length && other_nodes.length) {
31932             nodes= other_nodes;
31933         }
31934         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31935             return false;
31936         }
31937         
31938         return nodes[0];
31939     },
31940     
31941     
31942     createRange: function(sel)
31943     {
31944         // this has strange effects when using with 
31945         // top toolbar - not sure if it's a great idea.
31946         //this.editor.contentWindow.focus();
31947         if (typeof sel != "undefined") {
31948             try {
31949                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31950             } catch(e) {
31951                 return this.doc.createRange();
31952             }
31953         } else {
31954             return this.doc.createRange();
31955         }
31956     },
31957     getParentElement: function()
31958     {
31959         
31960         this.assignDocWin();
31961         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31962         
31963         var range = this.createRange(sel);
31964          
31965         try {
31966             var p = range.commonAncestorContainer;
31967             while (p.nodeType == 3) { // text node
31968                 p = p.parentNode;
31969             }
31970             return p;
31971         } catch (e) {
31972             return null;
31973         }
31974     
31975     },
31976     /***
31977      *
31978      * Range intersection.. the hard stuff...
31979      *  '-1' = before
31980      *  '0' = hits..
31981      *  '1' = after.
31982      *         [ -- selected range --- ]
31983      *   [fail]                        [fail]
31984      *
31985      *    basically..
31986      *      if end is before start or  hits it. fail.
31987      *      if start is after end or hits it fail.
31988      *
31989      *   if either hits (but other is outside. - then it's not 
31990      *   
31991      *    
31992      **/
31993     
31994     
31995     // @see http://www.thismuchiknow.co.uk/?p=64.
31996     rangeIntersectsNode : function(range, node)
31997     {
31998         var nodeRange = node.ownerDocument.createRange();
31999         try {
32000             nodeRange.selectNode(node);
32001         } catch (e) {
32002             nodeRange.selectNodeContents(node);
32003         }
32004     
32005         var rangeStartRange = range.cloneRange();
32006         rangeStartRange.collapse(true);
32007     
32008         var rangeEndRange = range.cloneRange();
32009         rangeEndRange.collapse(false);
32010     
32011         var nodeStartRange = nodeRange.cloneRange();
32012         nodeStartRange.collapse(true);
32013     
32014         var nodeEndRange = nodeRange.cloneRange();
32015         nodeEndRange.collapse(false);
32016     
32017         return rangeStartRange.compareBoundaryPoints(
32018                  Range.START_TO_START, nodeEndRange) == -1 &&
32019                rangeEndRange.compareBoundaryPoints(
32020                  Range.START_TO_START, nodeStartRange) == 1;
32021         
32022          
32023     },
32024     rangeCompareNode : function(range, node)
32025     {
32026         var nodeRange = node.ownerDocument.createRange();
32027         try {
32028             nodeRange.selectNode(node);
32029         } catch (e) {
32030             nodeRange.selectNodeContents(node);
32031         }
32032         
32033         
32034         range.collapse(true);
32035     
32036         nodeRange.collapse(true);
32037      
32038         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32039         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32040          
32041         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32042         
32043         var nodeIsBefore   =  ss == 1;
32044         var nodeIsAfter    = ee == -1;
32045         
32046         if (nodeIsBefore && nodeIsAfter) {
32047             return 0; // outer
32048         }
32049         if (!nodeIsBefore && nodeIsAfter) {
32050             return 1; //right trailed.
32051         }
32052         
32053         if (nodeIsBefore && !nodeIsAfter) {
32054             return 2;  // left trailed.
32055         }
32056         // fully contined.
32057         return 3;
32058     },
32059  
32060     cleanWordChars : function(input) {// change the chars to hex code
32061         
32062        var swapCodes  = [ 
32063             [    8211, "&#8211;" ], 
32064             [    8212, "&#8212;" ], 
32065             [    8216,  "'" ],  
32066             [    8217, "'" ],  
32067             [    8220, '"' ],  
32068             [    8221, '"' ],  
32069             [    8226, "*" ],  
32070             [    8230, "..." ]
32071         ]; 
32072         var output = input;
32073         Roo.each(swapCodes, function(sw) { 
32074             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32075             
32076             output = output.replace(swapper, sw[1]);
32077         });
32078         
32079         return output;
32080     },
32081     
32082      
32083     
32084         
32085     
32086     cleanUpChild : function (node)
32087     {
32088         
32089         new Roo.htmleditor.FilterComment({node : node});
32090         new Roo.htmleditor.FilterAttributes({
32091                 node : node,
32092                 attrib_black : this.ablack,
32093                 attrib_clean : this.aclean,
32094                 style_white : this.cwhite,
32095                 style_black : this.cblack
32096         });
32097         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32098         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32099          
32100         
32101     },
32102     
32103     /**
32104      * Clean up MS wordisms...
32105      * @deprecated - use filter directly
32106      */
32107     cleanWord : function(node)
32108     {
32109         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32110         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32111         
32112     },
32113    
32114     
32115     /**
32116
32117      * @deprecated - use filters
32118      */
32119     cleanTableWidths : function(node)
32120     {
32121         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32122         
32123  
32124     },
32125     
32126      
32127         
32128     applyBlacklists : function()
32129     {
32130         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32131         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32132         
32133         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32134         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32135         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32136         
32137         this.white = [];
32138         this.black = [];
32139         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32140             if (b.indexOf(tag) > -1) {
32141                 return;
32142             }
32143             this.white.push(tag);
32144             
32145         }, this);
32146         
32147         Roo.each(w, function(tag) {
32148             if (b.indexOf(tag) > -1) {
32149                 return;
32150             }
32151             if (this.white.indexOf(tag) > -1) {
32152                 return;
32153             }
32154             this.white.push(tag);
32155             
32156         }, this);
32157         
32158         
32159         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32160             if (w.indexOf(tag) > -1) {
32161                 return;
32162             }
32163             this.black.push(tag);
32164             
32165         }, this);
32166         
32167         Roo.each(b, function(tag) {
32168             if (w.indexOf(tag) > -1) {
32169                 return;
32170             }
32171             if (this.black.indexOf(tag) > -1) {
32172                 return;
32173             }
32174             this.black.push(tag);
32175             
32176         }, this);
32177         
32178         
32179         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32180         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32181         
32182         this.cwhite = [];
32183         this.cblack = [];
32184         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32185             if (b.indexOf(tag) > -1) {
32186                 return;
32187             }
32188             this.cwhite.push(tag);
32189             
32190         }, this);
32191         
32192         Roo.each(w, function(tag) {
32193             if (b.indexOf(tag) > -1) {
32194                 return;
32195             }
32196             if (this.cwhite.indexOf(tag) > -1) {
32197                 return;
32198             }
32199             this.cwhite.push(tag);
32200             
32201         }, this);
32202         
32203         
32204         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32205             if (w.indexOf(tag) > -1) {
32206                 return;
32207             }
32208             this.cblack.push(tag);
32209             
32210         }, this);
32211         
32212         Roo.each(b, function(tag) {
32213             if (w.indexOf(tag) > -1) {
32214                 return;
32215             }
32216             if (this.cblack.indexOf(tag) > -1) {
32217                 return;
32218             }
32219             this.cblack.push(tag);
32220             
32221         }, this);
32222     },
32223     
32224     setStylesheets : function(stylesheets)
32225     {
32226         if(typeof(stylesheets) == 'string'){
32227             Roo.get(this.iframe.contentDocument.head).createChild({
32228                 tag : 'link',
32229                 rel : 'stylesheet',
32230                 type : 'text/css',
32231                 href : stylesheets
32232             });
32233             
32234             return;
32235         }
32236         var _this = this;
32237      
32238         Roo.each(stylesheets, function(s) {
32239             if(!s.length){
32240                 return;
32241             }
32242             
32243             Roo.get(_this.iframe.contentDocument.head).createChild({
32244                 tag : 'link',
32245                 rel : 'stylesheet',
32246                 type : 'text/css',
32247                 href : s
32248             });
32249         });
32250
32251         
32252     },
32253     
32254     
32255     updateLanguage : function()
32256     {
32257         if (!this.iframe || !this.iframe.contentDocument) {
32258             return;
32259         }
32260         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32261     },
32262     
32263     
32264     removeStylesheets : function()
32265     {
32266         var _this = this;
32267         
32268         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32269             s.remove();
32270         });
32271     },
32272     
32273     setStyle : function(style)
32274     {
32275         Roo.get(this.iframe.contentDocument.head).createChild({
32276             tag : 'style',
32277             type : 'text/css',
32278             html : style
32279         });
32280
32281         return;
32282     }
32283     
32284     // hide stuff that is not compatible
32285     /**
32286      * @event blur
32287      * @hide
32288      */
32289     /**
32290      * @event change
32291      * @hide
32292      */
32293     /**
32294      * @event focus
32295      * @hide
32296      */
32297     /**
32298      * @event specialkey
32299      * @hide
32300      */
32301     /**
32302      * @cfg {String} fieldClass @hide
32303      */
32304     /**
32305      * @cfg {String} focusClass @hide
32306      */
32307     /**
32308      * @cfg {String} autoCreate @hide
32309      */
32310     /**
32311      * @cfg {String} inputType @hide
32312      */
32313     /**
32314      * @cfg {String} invalidClass @hide
32315      */
32316     /**
32317      * @cfg {String} invalidText @hide
32318      */
32319     /**
32320      * @cfg {String} msgFx @hide
32321      */
32322     /**
32323      * @cfg {String} validateOnBlur @hide
32324      */
32325 });
32326
32327 Roo.HtmlEditorCore.white = [
32328         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32329         
32330        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32331        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32332        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32333        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32334        'TABLE',   'UL',         'XMP', 
32335        
32336        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32337       'THEAD',   'TR', 
32338      
32339       'DIR', 'MENU', 'OL', 'UL', 'DL',
32340        
32341       'EMBED',  'OBJECT'
32342 ];
32343
32344
32345 Roo.HtmlEditorCore.black = [
32346     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32347         'APPLET', // 
32348         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32349         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32350         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32351         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32352         //'FONT' // CLEAN LATER..
32353         'COLGROUP', 'COL'   // messy tables.
32354         
32355         
32356 ];
32357 Roo.HtmlEditorCore.clean = [ // ?? needed???
32358      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32359 ];
32360 Roo.HtmlEditorCore.tag_remove = [
32361     'FONT', 'TBODY'  
32362 ];
32363 // attributes..
32364
32365 Roo.HtmlEditorCore.ablack = [
32366     'on'
32367 ];
32368     
32369 Roo.HtmlEditorCore.aclean = [ 
32370     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32371 ];
32372
32373 // protocols..
32374 Roo.HtmlEditorCore.pwhite= [
32375         'http',  'https',  'mailto'
32376 ];
32377
32378 // white listed style attributes.
32379 Roo.HtmlEditorCore.cwhite= [
32380       //  'text-align', /// default is to allow most things..
32381       
32382          
32383 //        'font-size'//??
32384 ];
32385
32386 // black listed style attributes.
32387 Roo.HtmlEditorCore.cblack= [
32388       //  'font-size' -- this can be set by the project 
32389 ];
32390
32391
32392
32393
32394     /*
32395  * - LGPL
32396  *
32397  * HtmlEditor
32398  * 
32399  */
32400
32401 /**
32402  * @class Roo.bootstrap.form.HtmlEditor
32403  * @extends Roo.bootstrap.form.TextArea
32404  * Bootstrap HtmlEditor class
32405
32406  * @constructor
32407  * Create a new HtmlEditor
32408  * @param {Object} config The config object
32409  */
32410
32411 Roo.bootstrap.form.HtmlEditor = function(config){
32412     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32413     if (!this.toolbars) {
32414         this.toolbars = [];
32415     }
32416     
32417     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32418     this.addEvents({
32419             /**
32420              * @event initialize
32421              * Fires when the editor is fully initialized (including the iframe)
32422              * @param {HtmlEditor} this
32423              */
32424             initialize: true,
32425             /**
32426              * @event activate
32427              * Fires when the editor is first receives the focus. Any insertion must wait
32428              * until after this event.
32429              * @param {HtmlEditor} this
32430              */
32431             activate: true,
32432              /**
32433              * @event beforesync
32434              * Fires before the textarea is updated with content from the editor iframe. Return false
32435              * to cancel the sync.
32436              * @param {HtmlEditor} this
32437              * @param {String} html
32438              */
32439             beforesync: true,
32440              /**
32441              * @event beforepush
32442              * Fires before the iframe editor is updated with content from the textarea. Return false
32443              * to cancel the push.
32444              * @param {HtmlEditor} this
32445              * @param {String} html
32446              */
32447             beforepush: true,
32448              /**
32449              * @event sync
32450              * Fires when the textarea is updated with content from the editor iframe.
32451              * @param {HtmlEditor} this
32452              * @param {String} html
32453              */
32454             sync: true,
32455              /**
32456              * @event push
32457              * Fires when the iframe editor is updated with content from the textarea.
32458              * @param {HtmlEditor} this
32459              * @param {String} html
32460              */
32461             push: true,
32462              /**
32463              * @event editmodechange
32464              * Fires when the editor switches edit modes
32465              * @param {HtmlEditor} this
32466              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32467              */
32468             editmodechange: true,
32469             /**
32470              * @event editorevent
32471              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32472              * @param {HtmlEditor} this
32473              */
32474             editorevent: true,
32475             /**
32476              * @event firstfocus
32477              * Fires when on first focus - needed by toolbars..
32478              * @param {HtmlEditor} this
32479              */
32480             firstfocus: true,
32481             /**
32482              * @event autosave
32483              * Auto save the htmlEditor value as a file into Events
32484              * @param {HtmlEditor} this
32485              */
32486             autosave: true,
32487             /**
32488              * @event savedpreview
32489              * preview the saved version of htmlEditor
32490              * @param {HtmlEditor} this
32491              */
32492             savedpreview: true
32493         });
32494 };
32495
32496
32497 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32498     
32499     
32500       /**
32501      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32502      */
32503     toolbars : false,
32504     
32505      /**
32506     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32507     */
32508     btns : [],
32509    
32510      /**
32511      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32512      *                        Roo.resizable.
32513      */
32514     resizable : false,
32515      /**
32516      * @cfg {Number} height (in pixels)
32517      */   
32518     height: 300,
32519    /**
32520      * @cfg {Number} width (in pixels)
32521      */   
32522     width: false,
32523     
32524     /**
32525      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32526      * 
32527      */
32528     stylesheets: false,
32529     
32530     // id of frame..
32531     frameId: false,
32532     
32533     // private properties
32534     validationEvent : false,
32535     deferHeight: true,
32536     initialized : false,
32537     activated : false,
32538     
32539     onFocus : Roo.emptyFn,
32540     iframePad:3,
32541     hideMode:'offsets',
32542     
32543     tbContainer : false,
32544     
32545     bodyCls : '',
32546     
32547     toolbarContainer :function() {
32548         return this.wrap.select('.x-html-editor-tb',true).first();
32549     },
32550
32551     /**
32552      * Protected method that will not generally be called directly. It
32553      * is called when the editor creates its toolbar. Override this method if you need to
32554      * add custom toolbar buttons.
32555      * @param {HtmlEditor} editor
32556      */
32557     createToolbar : function(){
32558         Roo.log('renewing');
32559         Roo.log("create toolbars");
32560         
32561         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32562         this.toolbars[0].render(this.toolbarContainer());
32563         
32564         return;
32565         
32566 //        if (!editor.toolbars || !editor.toolbars.length) {
32567 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32568 //        }
32569 //        
32570 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32571 //            editor.toolbars[i] = Roo.factory(
32572 //                    typeof(editor.toolbars[i]) == 'string' ?
32573 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32574 //                Roo.bootstrap.form.HtmlEditor);
32575 //            editor.toolbars[i].init(editor);
32576 //        }
32577     },
32578
32579      
32580     // private
32581     onRender : function(ct, position)
32582     {
32583        // Roo.log("Call onRender: " + this.xtype);
32584         var _t = this;
32585         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32586       
32587         this.wrap = this.inputEl().wrap({
32588             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32589         });
32590         
32591         this.editorcore.onRender(ct, position);
32592          
32593         if (this.resizable) {
32594             this.resizeEl = new Roo.Resizable(this.wrap, {
32595                 pinned : true,
32596                 wrap: true,
32597                 dynamic : true,
32598                 minHeight : this.height,
32599                 height: this.height,
32600                 handles : this.resizable,
32601                 width: this.width,
32602                 listeners : {
32603                     resize : function(r, w, h) {
32604                         _t.onResize(w,h); // -something
32605                     }
32606                 }
32607             });
32608             
32609         }
32610         this.createToolbar(this);
32611        
32612         
32613         if(!this.width && this.resizable){
32614             this.setSize(this.wrap.getSize());
32615         }
32616         if (this.resizeEl) {
32617             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32618             // should trigger onReize..
32619         }
32620         
32621     },
32622
32623     // private
32624     onResize : function(w, h)
32625     {
32626         Roo.log('resize: ' +w + ',' + h );
32627         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32628         var ew = false;
32629         var eh = false;
32630         
32631         if(this.inputEl() ){
32632             if(typeof w == 'number'){
32633                 var aw = w - this.wrap.getFrameWidth('lr');
32634                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32635                 ew = aw;
32636             }
32637             if(typeof h == 'number'){
32638                  var tbh = -11;  // fixme it needs to tool bar size!
32639                 for (var i =0; i < this.toolbars.length;i++) {
32640                     // fixme - ask toolbars for heights?
32641                     tbh += this.toolbars[i].el.getHeight();
32642                     //if (this.toolbars[i].footer) {
32643                     //    tbh += this.toolbars[i].footer.el.getHeight();
32644                     //}
32645                 }
32646               
32647                 
32648                 
32649                 
32650                 
32651                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32652                 ah -= 5; // knock a few pixes off for look..
32653                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32654                 var eh = ah;
32655             }
32656         }
32657         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32658         this.editorcore.onResize(ew,eh);
32659         
32660     },
32661
32662     /**
32663      * Toggles the editor between standard and source edit mode.
32664      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32665      */
32666     toggleSourceEdit : function(sourceEditMode)
32667     {
32668         this.editorcore.toggleSourceEdit(sourceEditMode);
32669         
32670         if(this.editorcore.sourceEditMode){
32671             Roo.log('editor - showing textarea');
32672             
32673 //            Roo.log('in');
32674 //            Roo.log(this.syncValue());
32675             this.syncValue();
32676             this.inputEl().removeClass(['hide', 'x-hidden']);
32677             this.inputEl().dom.removeAttribute('tabIndex');
32678             this.inputEl().focus();
32679         }else{
32680             Roo.log('editor - hiding textarea');
32681 //            Roo.log('out')
32682 //            Roo.log(this.pushValue()); 
32683             this.pushValue();
32684             
32685             this.inputEl().addClass(['hide', 'x-hidden']);
32686             this.inputEl().dom.setAttribute('tabIndex', -1);
32687             //this.deferFocus();
32688         }
32689          
32690         if(this.resizable){
32691             this.setSize(this.wrap.getSize());
32692         }
32693         
32694         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32695     },
32696  
32697     // private (for BoxComponent)
32698     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32699
32700     // private (for BoxComponent)
32701     getResizeEl : function(){
32702         return this.wrap;
32703     },
32704
32705     // private (for BoxComponent)
32706     getPositionEl : function(){
32707         return this.wrap;
32708     },
32709
32710     // private
32711     initEvents : function(){
32712         this.originalValue = this.getValue();
32713     },
32714
32715 //    /**
32716 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32717 //     * @method
32718 //     */
32719 //    markInvalid : Roo.emptyFn,
32720 //    /**
32721 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32722 //     * @method
32723 //     */
32724 //    clearInvalid : Roo.emptyFn,
32725
32726     setValue : function(v){
32727         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32728         this.editorcore.pushValue();
32729     },
32730
32731      
32732     // private
32733     deferFocus : function(){
32734         this.focus.defer(10, this);
32735     },
32736
32737     // doc'ed in Field
32738     focus : function(){
32739         this.editorcore.focus();
32740         
32741     },
32742       
32743
32744     // private
32745     onDestroy : function(){
32746         
32747         
32748         
32749         if(this.rendered){
32750             
32751             for (var i =0; i < this.toolbars.length;i++) {
32752                 // fixme - ask toolbars for heights?
32753                 this.toolbars[i].onDestroy();
32754             }
32755             
32756             this.wrap.dom.innerHTML = '';
32757             this.wrap.remove();
32758         }
32759     },
32760
32761     // private
32762     onFirstFocus : function(){
32763         //Roo.log("onFirstFocus");
32764         this.editorcore.onFirstFocus();
32765          for (var i =0; i < this.toolbars.length;i++) {
32766             this.toolbars[i].onFirstFocus();
32767         }
32768         
32769     },
32770     
32771     // private
32772     syncValue : function()
32773     {   
32774         this.editorcore.syncValue();
32775     },
32776     
32777     pushValue : function()
32778     {   
32779         this.editorcore.pushValue();
32780     }
32781      
32782     
32783     // hide stuff that is not compatible
32784     /**
32785      * @event blur
32786      * @hide
32787      */
32788     /**
32789      * @event change
32790      * @hide
32791      */
32792     /**
32793      * @event focus
32794      * @hide
32795      */
32796     /**
32797      * @event specialkey
32798      * @hide
32799      */
32800     /**
32801      * @cfg {String} fieldClass @hide
32802      */
32803     /**
32804      * @cfg {String} focusClass @hide
32805      */
32806     /**
32807      * @cfg {String} autoCreate @hide
32808      */
32809     /**
32810      * @cfg {String} inputType @hide
32811      */
32812      
32813     /**
32814      * @cfg {String} invalidText @hide
32815      */
32816     /**
32817      * @cfg {String} msgFx @hide
32818      */
32819     /**
32820      * @cfg {String} validateOnBlur @hide
32821      */
32822 });
32823  
32824     
32825    
32826    
32827    
32828       
32829 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32830 /**
32831  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32832  * @parent Roo.bootstrap.form.HtmlEditor
32833  * @extends Roo.bootstrap.nav.Simplebar
32834  * Basic Toolbar
32835  * 
32836  * @example
32837  * Usage:
32838  *
32839  new Roo.bootstrap.form.HtmlEditor({
32840     ....
32841     toolbars : [
32842         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32843             disable : { fonts: 1 , format: 1, ..., ... , ...],
32844             btns : [ .... ]
32845         })
32846     }
32847      
32848  * 
32849  * @cfg {Object} disable List of elements to disable..
32850  * @cfg {Array} btns List of additional buttons.
32851  * 
32852  * 
32853  * NEEDS Extra CSS? 
32854  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32855  */
32856  
32857 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32858 {
32859     
32860     Roo.apply(this, config);
32861     
32862     // default disabled, based on 'good practice'..
32863     this.disable = this.disable || {};
32864     Roo.applyIf(this.disable, {
32865         fontSize : true,
32866         colors : true,
32867         specialElements : true
32868     });
32869     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32870     
32871     this.editor = config.editor;
32872     this.editorcore = config.editor.editorcore;
32873     
32874     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32875     
32876     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32877     // dont call parent... till later.
32878 }
32879 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32880      
32881     bar : true,
32882     
32883     editor : false,
32884     editorcore : false,
32885     
32886     
32887     formats : [
32888         "p" ,  
32889         "h1","h2","h3","h4","h5","h6", 
32890         "pre", "code", 
32891         "abbr", "acronym", "address", "cite", "samp", "var",
32892         'div','span'
32893     ],
32894     
32895     onRender : function(ct, position)
32896     {
32897        // Roo.log("Call onRender: " + this.xtype);
32898         
32899        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32900        Roo.log(this.el);
32901        this.el.dom.style.marginBottom = '0';
32902        var _this = this;
32903        var editorcore = this.editorcore;
32904        var editor= this.editor;
32905        
32906        var children = [];
32907        var btn = function(id,cmd , toggle, handler, html){
32908        
32909             var  event = toggle ? 'toggle' : 'click';
32910        
32911             var a = {
32912                 size : 'sm',
32913                 xtype: 'Button',
32914                 xns: Roo.bootstrap,
32915                 //glyphicon : id,
32916                 fa: id,
32917                 cmd : id || cmd,
32918                 enableToggle:toggle !== false,
32919                 html : html || '',
32920                 pressed : toggle ? false : null,
32921                 listeners : {}
32922             };
32923             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32924                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32925             };
32926             children.push(a);
32927             return a;
32928        }
32929        
32930     //    var cb_box = function...
32931         
32932         var style = {
32933                 xtype: 'Button',
32934                 size : 'sm',
32935                 xns: Roo.bootstrap,
32936                 fa : 'font',
32937                 //html : 'submit'
32938                 menu : {
32939                     xtype: 'Menu',
32940                     xns: Roo.bootstrap,
32941                     items:  []
32942                 }
32943         };
32944         Roo.each(this.formats, function(f) {
32945             style.menu.items.push({
32946                 xtype :'MenuItem',
32947                 xns: Roo.bootstrap,
32948                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32949                 tagname : f,
32950                 listeners : {
32951                     click : function()
32952                     {
32953                         editorcore.insertTag(this.tagname);
32954                         editor.focus();
32955                     }
32956                 }
32957                 
32958             });
32959         });
32960         children.push(style);   
32961         
32962         btn('bold',false,true);
32963         btn('italic',false,true);
32964         btn('align-left', 'justifyleft',true);
32965         btn('align-center', 'justifycenter',true);
32966         btn('align-right' , 'justifyright',true);
32967         btn('link', false, false, function(btn) {
32968             //Roo.log("create link?");
32969             var url = prompt(this.createLinkText, this.defaultLinkValue);
32970             if(url && url != 'http:/'+'/'){
32971                 this.editorcore.relayCmd('createlink', url);
32972             }
32973         }),
32974         btn('list','insertunorderedlist',true);
32975         btn('pencil', false,true, function(btn){
32976                 Roo.log(this);
32977                 this.toggleSourceEdit(btn.pressed);
32978         });
32979         
32980         if (this.editor.btns.length > 0) {
32981             for (var i = 0; i<this.editor.btns.length; i++) {
32982                 children.push(this.editor.btns[i]);
32983             }
32984         }
32985         
32986         /*
32987         var cog = {
32988                 xtype: 'Button',
32989                 size : 'sm',
32990                 xns: Roo.bootstrap,
32991                 glyphicon : 'cog',
32992                 //html : 'submit'
32993                 menu : {
32994                     xtype: 'Menu',
32995                     xns: Roo.bootstrap,
32996                     items:  []
32997                 }
32998         };
32999         
33000         cog.menu.items.push({
33001             xtype :'MenuItem',
33002             xns: Roo.bootstrap,
33003             html : Clean styles,
33004             tagname : f,
33005             listeners : {
33006                 click : function()
33007                 {
33008                     editorcore.insertTag(this.tagname);
33009                     editor.focus();
33010                 }
33011             }
33012             
33013         });
33014        */
33015         
33016          
33017        this.xtype = 'NavSimplebar';
33018         
33019         for(var i=0;i< children.length;i++) {
33020             
33021             this.buttons.add(this.addxtypeChild(children[i]));
33022             
33023         }
33024         
33025         editor.on('editorevent', this.updateToolbar, this);
33026     },
33027     onBtnClick : function(id)
33028     {
33029        this.editorcore.relayCmd(id);
33030        this.editorcore.focus();
33031     },
33032     
33033     /**
33034      * Protected method that will not generally be called directly. It triggers
33035      * a toolbar update by reading the markup state of the current selection in the editor.
33036      */
33037     updateToolbar: function(){
33038
33039         if(!this.editorcore.activated){
33040             this.editor.onFirstFocus(); // is this neeed?
33041             return;
33042         }
33043
33044         var btns = this.buttons; 
33045         var doc = this.editorcore.doc;
33046         btns.get('bold').setActive(doc.queryCommandState('bold'));
33047         btns.get('italic').setActive(doc.queryCommandState('italic'));
33048         //btns.get('underline').setActive(doc.queryCommandState('underline'));
33049         
33050         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
33051         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
33052         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
33053         
33054         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
33055         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
33056          /*
33057         
33058         var ans = this.editorcore.getAllAncestors();
33059         if (this.formatCombo) {
33060             
33061             
33062             var store = this.formatCombo.store;
33063             this.formatCombo.setValue("");
33064             for (var i =0; i < ans.length;i++) {
33065                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
33066                     // select it..
33067                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33068                     break;
33069                 }
33070             }
33071         }
33072         
33073         
33074         
33075         // hides menus... - so this cant be on a menu...
33076         Roo.bootstrap.MenuMgr.hideAll();
33077         */
33078         Roo.bootstrap.menu.Manager.hideAll();
33079         //this.editorsyncValue();
33080     },
33081     onFirstFocus: function() {
33082         this.buttons.each(function(item){
33083            item.enable();
33084         });
33085     },
33086     toggleSourceEdit : function(sourceEditMode){
33087         
33088           
33089         if(sourceEditMode){
33090             Roo.log("disabling buttons");
33091            this.buttons.each( function(item){
33092                 if(item.cmd != 'pencil'){
33093                     item.disable();
33094                 }
33095             });
33096           
33097         }else{
33098             Roo.log("enabling buttons");
33099             if(this.editorcore.initialized){
33100                 this.buttons.each( function(item){
33101                     item.enable();
33102                 });
33103             }
33104             
33105         }
33106         Roo.log("calling toggole on editor");
33107         // tell the editor that it's been pressed..
33108         this.editor.toggleSourceEdit(sourceEditMode);
33109        
33110     }
33111 });
33112
33113
33114
33115
33116  
33117 /*
33118  * - LGPL
33119  */
33120
33121 /**
33122  * @class Roo.bootstrap.form.Markdown
33123  * @extends Roo.bootstrap.form.TextArea
33124  * Bootstrap Showdown editable area
33125  * @cfg {string} content
33126  * 
33127  * @constructor
33128  * Create a new Showdown
33129  */
33130
33131 Roo.bootstrap.form.Markdown = function(config){
33132     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33133    
33134 };
33135
33136 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33137     
33138     editing :false,
33139     
33140     initEvents : function()
33141     {
33142         
33143         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33144         this.markdownEl = this.el.createChild({
33145             cls : 'roo-markdown-area'
33146         });
33147         this.inputEl().addClass('d-none');
33148         if (this.getValue() == '') {
33149             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33150             
33151         } else {
33152             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33153         }
33154         this.markdownEl.on('click', this.toggleTextEdit, this);
33155         this.on('blur', this.toggleTextEdit, this);
33156         this.on('specialkey', this.resizeTextArea, this);
33157     },
33158     
33159     toggleTextEdit : function()
33160     {
33161         var sh = this.markdownEl.getHeight();
33162         this.inputEl().addClass('d-none');
33163         this.markdownEl.addClass('d-none');
33164         if (!this.editing) {
33165             // show editor?
33166             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33167             this.inputEl().removeClass('d-none');
33168             this.inputEl().focus();
33169             this.editing = true;
33170             return;
33171         }
33172         // show showdown...
33173         this.updateMarkdown();
33174         this.markdownEl.removeClass('d-none');
33175         this.editing = false;
33176         return;
33177     },
33178     updateMarkdown : function()
33179     {
33180         if (this.getValue() == '') {
33181             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33182             return;
33183         }
33184  
33185         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33186     },
33187     
33188     resizeTextArea: function () {
33189         
33190         var sh = 100;
33191         Roo.log([sh, this.getValue().split("\n").length * 30]);
33192         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33193     },
33194     setValue : function(val)
33195     {
33196         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33197         if (!this.editing) {
33198             this.updateMarkdown();
33199         }
33200         
33201     },
33202     focus : function()
33203     {
33204         if (!this.editing) {
33205             this.toggleTextEdit();
33206         }
33207         
33208     }
33209
33210
33211 });/*
33212  * Based on:
33213  * Ext JS Library 1.1.1
33214  * Copyright(c) 2006-2007, Ext JS, LLC.
33215  *
33216  * Originally Released Under LGPL - original licence link has changed is not relivant.
33217  *
33218  * Fork - LGPL
33219  * <script type="text/javascript">
33220  */
33221  
33222 /**
33223  * @class Roo.bootstrap.PagingToolbar
33224  * @extends Roo.bootstrap.nav.Simplebar
33225  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33226  * @constructor
33227  * Create a new PagingToolbar
33228  * @param {Object} config The config object
33229  * @param {Roo.data.Store} store
33230  */
33231 Roo.bootstrap.PagingToolbar = function(config)
33232 {
33233     // old args format still supported... - xtype is prefered..
33234         // created from xtype...
33235     
33236     this.ds = config.dataSource;
33237     
33238     if (config.store && !this.ds) {
33239         this.store= Roo.factory(config.store, Roo.data);
33240         this.ds = this.store;
33241         this.ds.xmodule = this.xmodule || false;
33242     }
33243     
33244     this.toolbarItems = [];
33245     if (config.items) {
33246         this.toolbarItems = config.items;
33247     }
33248     
33249     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33250     
33251     this.cursor = 0;
33252     
33253     if (this.ds) { 
33254         this.bind(this.ds);
33255     }
33256     
33257     if (Roo.bootstrap.version == 4) {
33258         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33259     } else {
33260         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33261     }
33262     
33263 };
33264
33265 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33266     /**
33267      * @cfg {Roo.bootstrap.Button} buttons[]
33268      * Buttons for the toolbar
33269      */
33270      /**
33271      * @cfg {Roo.data.Store} store
33272      * The underlying data store providing the paged data
33273      */
33274     /**
33275      * @cfg {String/HTMLElement/Element} container
33276      * container The id or element that will contain the toolbar
33277      */
33278     /**
33279      * @cfg {Boolean} displayInfo
33280      * True to display the displayMsg (defaults to false)
33281      */
33282     /**
33283      * @cfg {Number} pageSize
33284      * The number of records to display per page (defaults to 20)
33285      */
33286     pageSize: 20,
33287     /**
33288      * @cfg {String} displayMsg
33289      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33290      */
33291     displayMsg : 'Displaying {0} - {1} of {2}',
33292     /**
33293      * @cfg {String} emptyMsg
33294      * The message to display when no records are found (defaults to "No data to display")
33295      */
33296     emptyMsg : 'No data to display',
33297     /**
33298      * Customizable piece of the default paging text (defaults to "Page")
33299      * @type String
33300      */
33301     beforePageText : "Page",
33302     /**
33303      * Customizable piece of the default paging text (defaults to "of %0")
33304      * @type String
33305      */
33306     afterPageText : "of {0}",
33307     /**
33308      * Customizable piece of the default paging text (defaults to "First Page")
33309      * @type String
33310      */
33311     firstText : "First Page",
33312     /**
33313      * Customizable piece of the default paging text (defaults to "Previous Page")
33314      * @type String
33315      */
33316     prevText : "Previous Page",
33317     /**
33318      * Customizable piece of the default paging text (defaults to "Next Page")
33319      * @type String
33320      */
33321     nextText : "Next Page",
33322     /**
33323      * Customizable piece of the default paging text (defaults to "Last Page")
33324      * @type String
33325      */
33326     lastText : "Last Page",
33327     /**
33328      * Customizable piece of the default paging text (defaults to "Refresh")
33329      * @type String
33330      */
33331     refreshText : "Refresh",
33332
33333     buttons : false,
33334     // private
33335     onRender : function(ct, position) 
33336     {
33337         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33338         this.navgroup.parentId = this.id;
33339         this.navgroup.onRender(this.el, null);
33340         // add the buttons to the navgroup
33341         
33342         if(this.displayInfo){
33343             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33344             this.displayEl = this.el.select('.x-paging-info', true).first();
33345 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33346 //            this.displayEl = navel.el.select('span',true).first();
33347         }
33348         
33349         var _this = this;
33350         
33351         if(this.buttons){
33352             Roo.each(_this.buttons, function(e){ // this might need to use render????
33353                Roo.factory(e).render(_this.el);
33354             });
33355         }
33356             
33357         Roo.each(_this.toolbarItems, function(e) {
33358             _this.navgroup.addItem(e);
33359         });
33360         
33361         
33362         this.first = this.navgroup.addItem({
33363             tooltip: this.firstText,
33364             cls: "prev btn-outline-secondary",
33365             html : ' <i class="fa fa-step-backward"></i>',
33366             disabled: true,
33367             preventDefault: true,
33368             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33369         });
33370         
33371         this.prev =  this.navgroup.addItem({
33372             tooltip: this.prevText,
33373             cls: "prev btn-outline-secondary",
33374             html : ' <i class="fa fa-backward"></i>',
33375             disabled: true,
33376             preventDefault: true,
33377             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33378         });
33379     //this.addSeparator();
33380         
33381         
33382         var field = this.navgroup.addItem( {
33383             tagtype : 'span',
33384             cls : 'x-paging-position  btn-outline-secondary',
33385              disabled: true,
33386             html : this.beforePageText  +
33387                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33388                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33389          } ); //?? escaped?
33390         
33391         this.field = field.el.select('input', true).first();
33392         this.field.on("keydown", this.onPagingKeydown, this);
33393         this.field.on("focus", function(){this.dom.select();});
33394     
33395     
33396         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33397         //this.field.setHeight(18);
33398         //this.addSeparator();
33399         this.next = this.navgroup.addItem({
33400             tooltip: this.nextText,
33401             cls: "next btn-outline-secondary",
33402             html : ' <i class="fa fa-forward"></i>',
33403             disabled: true,
33404             preventDefault: true,
33405             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33406         });
33407         this.last = this.navgroup.addItem({
33408             tooltip: this.lastText,
33409             html : ' <i class="fa fa-step-forward"></i>',
33410             cls: "next btn-outline-secondary",
33411             disabled: true,
33412             preventDefault: true,
33413             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33414         });
33415     //this.addSeparator();
33416         this.loading = this.navgroup.addItem({
33417             tooltip: this.refreshText,
33418             cls: "btn-outline-secondary",
33419             html : ' <i class="fa fa-refresh"></i>',
33420             preventDefault: true,
33421             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33422         });
33423         
33424     },
33425
33426     // private
33427     updateInfo : function(){
33428         if(this.displayEl){
33429             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33430             var msg = count == 0 ?
33431                 this.emptyMsg :
33432                 String.format(
33433                     this.displayMsg,
33434                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33435                 );
33436             this.displayEl.update(msg);
33437         }
33438     },
33439
33440     // private
33441     onLoad : function(ds, r, o)
33442     {
33443         this.cursor = o.params && o.params.start ? o.params.start : 0;
33444         
33445         var d = this.getPageData(),
33446             ap = d.activePage,
33447             ps = d.pages;
33448         
33449         
33450         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33451         this.field.dom.value = ap;
33452         this.first.setDisabled(ap == 1);
33453         this.prev.setDisabled(ap == 1);
33454         this.next.setDisabled(ap == ps);
33455         this.last.setDisabled(ap == ps);
33456         this.loading.enable();
33457         this.updateInfo();
33458     },
33459
33460     // private
33461     getPageData : function(){
33462         var total = this.ds.getTotalCount();
33463         return {
33464             total : total,
33465             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33466             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33467         };
33468     },
33469
33470     // private
33471     onLoadError : function(proxy, o){
33472         this.loading.enable();
33473         if (this.ds.events.loadexception.listeners.length  < 2) {
33474             // nothing has been assigned to loadexception except this...
33475             // so 
33476             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33477
33478         }
33479     },
33480
33481     // private
33482     onPagingKeydown : function(e){
33483         var k = e.getKey();
33484         var d = this.getPageData();
33485         if(k == e.RETURN){
33486             var v = this.field.dom.value, pageNum;
33487             if(!v || isNaN(pageNum = parseInt(v, 10))){
33488                 this.field.dom.value = d.activePage;
33489                 return;
33490             }
33491             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33492             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33493             e.stopEvent();
33494         }
33495         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))
33496         {
33497           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33498           this.field.dom.value = pageNum;
33499           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33500           e.stopEvent();
33501         }
33502         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33503         {
33504           var v = this.field.dom.value, pageNum; 
33505           var increment = (e.shiftKey) ? 10 : 1;
33506           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33507                 increment *= -1;
33508           }
33509           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33510             this.field.dom.value = d.activePage;
33511             return;
33512           }
33513           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33514           {
33515             this.field.dom.value = parseInt(v, 10) + increment;
33516             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33517             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33518           }
33519           e.stopEvent();
33520         }
33521     },
33522
33523     // private
33524     beforeLoad : function(){
33525         if(this.loading){
33526             this.loading.disable();
33527         }
33528     },
33529
33530     // private
33531     onClick : function(which){
33532         
33533         var ds = this.ds;
33534         if (!ds) {
33535             return;
33536         }
33537         
33538         switch(which){
33539             case "first":
33540                 ds.load({params:{start: 0, limit: this.pageSize}});
33541             break;
33542             case "prev":
33543                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33544             break;
33545             case "next":
33546                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33547             break;
33548             case "last":
33549                 var total = ds.getTotalCount();
33550                 var extra = total % this.pageSize;
33551                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33552                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33553             break;
33554             case "refresh":
33555                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33556             break;
33557         }
33558     },
33559
33560     /**
33561      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33562      * @param {Roo.data.Store} store The data store to unbind
33563      */
33564     unbind : function(ds){
33565         ds.un("beforeload", this.beforeLoad, this);
33566         ds.un("load", this.onLoad, this);
33567         ds.un("loadexception", this.onLoadError, this);
33568         ds.un("remove", this.updateInfo, this);
33569         ds.un("add", this.updateInfo, this);
33570         this.ds = undefined;
33571     },
33572
33573     /**
33574      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33575      * @param {Roo.data.Store} store The data store to bind
33576      */
33577     bind : function(ds){
33578         ds.on("beforeload", this.beforeLoad, this);
33579         ds.on("load", this.onLoad, this);
33580         ds.on("loadexception", this.onLoadError, this);
33581         ds.on("remove", this.updateInfo, this);
33582         ds.on("add", this.updateInfo, this);
33583         this.ds = ds;
33584     }
33585 });/*
33586  * - LGPL
33587  *
33588  * element
33589  * 
33590  */
33591
33592 /**
33593  * @class Roo.bootstrap.MessageBar
33594  * @extends Roo.bootstrap.Component
33595  * Bootstrap MessageBar class
33596  * @cfg {String} html contents of the MessageBar
33597  * @cfg {String} weight (info | success | warning | danger) default info
33598  * @cfg {String} beforeClass insert the bar before the given class
33599  * @cfg {Boolean} closable (true | false) default false
33600  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33601  * 
33602  * @constructor
33603  * Create a new Element
33604  * @param {Object} config The config object
33605  */
33606
33607 Roo.bootstrap.MessageBar = function(config){
33608     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33609 };
33610
33611 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33612     
33613     html: '',
33614     weight: 'info',
33615     closable: false,
33616     fixed: false,
33617     beforeClass: 'bootstrap-sticky-wrap',
33618     
33619     getAutoCreate : function(){
33620         
33621         var cfg = {
33622             tag: 'div',
33623             cls: 'alert alert-dismissable alert-' + this.weight,
33624             cn: [
33625                 {
33626                     tag: 'span',
33627                     cls: 'message',
33628                     html: this.html || ''
33629                 }
33630             ]
33631         };
33632         
33633         if(this.fixed){
33634             cfg.cls += ' alert-messages-fixed';
33635         }
33636         
33637         if(this.closable){
33638             cfg.cn.push({
33639                 tag: 'button',
33640                 cls: 'close',
33641                 html: 'x'
33642             });
33643         }
33644         
33645         return cfg;
33646     },
33647     
33648     onRender : function(ct, position)
33649     {
33650         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33651         
33652         if(!this.el){
33653             var cfg = Roo.apply({},  this.getAutoCreate());
33654             cfg.id = Roo.id();
33655             
33656             if (this.cls) {
33657                 cfg.cls += ' ' + this.cls;
33658             }
33659             if (this.style) {
33660                 cfg.style = this.style;
33661             }
33662             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33663             
33664             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33665         }
33666         
33667         this.el.select('>button.close').on('click', this.hide, this);
33668         
33669     },
33670     
33671     show : function()
33672     {
33673         if (!this.rendered) {
33674             this.render();
33675         }
33676         
33677         this.el.show();
33678         
33679         this.fireEvent('show', this);
33680         
33681     },
33682     
33683     hide : function()
33684     {
33685         if (!this.rendered) {
33686             this.render();
33687         }
33688         
33689         this.el.hide();
33690         
33691         this.fireEvent('hide', this);
33692     },
33693     
33694     update : function()
33695     {
33696 //        var e = this.el.dom.firstChild;
33697 //        
33698 //        if(this.closable){
33699 //            e = e.nextSibling;
33700 //        }
33701 //        
33702 //        e.data = this.html || '';
33703
33704         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33705     }
33706    
33707 });
33708
33709  
33710
33711      /*
33712  * - LGPL
33713  *
33714  * Graph
33715  * 
33716  */
33717
33718
33719 /**
33720  * @class Roo.bootstrap.Graph
33721  * @extends Roo.bootstrap.Component
33722  * Bootstrap Graph class
33723 > Prameters
33724  -sm {number} sm 4
33725  -md {number} md 5
33726  @cfg {String} graphtype  bar | vbar | pie
33727  @cfg {number} g_x coodinator | centre x (pie)
33728  @cfg {number} g_y coodinator | centre y (pie)
33729  @cfg {number} g_r radius (pie)
33730  @cfg {number} g_height height of the chart (respected by all elements in the set)
33731  @cfg {number} g_width width of the chart (respected by all elements in the set)
33732  @cfg {Object} title The title of the chart
33733     
33734  -{Array}  values
33735  -opts (object) options for the chart 
33736      o {
33737      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33738      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33739      o vgutter (number)
33740      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.
33741      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33742      o to
33743      o stretch (boolean)
33744      o }
33745  -opts (object) options for the pie
33746      o{
33747      o cut
33748      o startAngle (number)
33749      o endAngle (number)
33750      } 
33751  *
33752  * @constructor
33753  * Create a new Input
33754  * @param {Object} config The config object
33755  */
33756
33757 Roo.bootstrap.Graph = function(config){
33758     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33759     
33760     this.addEvents({
33761         // img events
33762         /**
33763          * @event click
33764          * The img click event for the img.
33765          * @param {Roo.EventObject} e
33766          */
33767         "click" : true
33768     });
33769 };
33770
33771 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33772     
33773     sm: 4,
33774     md: 5,
33775     graphtype: 'bar',
33776     g_height: 250,
33777     g_width: 400,
33778     g_x: 50,
33779     g_y: 50,
33780     g_r: 30,
33781     opts:{
33782         //g_colors: this.colors,
33783         g_type: 'soft',
33784         g_gutter: '20%'
33785
33786     },
33787     title : false,
33788
33789     getAutoCreate : function(){
33790         
33791         var cfg = {
33792             tag: 'div',
33793             html : null
33794         };
33795         
33796         
33797         return  cfg;
33798     },
33799
33800     onRender : function(ct,position){
33801         
33802         
33803         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33804         
33805         if (typeof(Raphael) == 'undefined') {
33806             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33807             return;
33808         }
33809         
33810         this.raphael = Raphael(this.el.dom);
33811         
33812                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33813                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33814                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33815                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33816                 /*
33817                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33818                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33819                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33820                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33821                 
33822                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33823                 r.barchart(330, 10, 300, 220, data1);
33824                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33825                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33826                 */
33827                 
33828                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33829                 // r.barchart(30, 30, 560, 250,  xdata, {
33830                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33831                 //     axis : "0 0 1 1",
33832                 //     axisxlabels :  xdata
33833                 //     //yvalues : cols,
33834                    
33835                 // });
33836 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33837 //        
33838 //        this.load(null,xdata,{
33839 //                axis : "0 0 1 1",
33840 //                axisxlabels :  xdata
33841 //                });
33842
33843     },
33844
33845     load : function(graphtype,xdata,opts)
33846     {
33847         this.raphael.clear();
33848         if(!graphtype) {
33849             graphtype = this.graphtype;
33850         }
33851         if(!opts){
33852             opts = this.opts;
33853         }
33854         var r = this.raphael,
33855             fin = function () {
33856                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33857             },
33858             fout = function () {
33859                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33860             },
33861             pfin = function() {
33862                 this.sector.stop();
33863                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33864
33865                 if (this.label) {
33866                     this.label[0].stop();
33867                     this.label[0].attr({ r: 7.5 });
33868                     this.label[1].attr({ "font-weight": 800 });
33869                 }
33870             },
33871             pfout = function() {
33872                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33873
33874                 if (this.label) {
33875                     this.label[0].animate({ r: 5 }, 500, "bounce");
33876                     this.label[1].attr({ "font-weight": 400 });
33877                 }
33878             };
33879
33880         switch(graphtype){
33881             case 'bar':
33882                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33883                 break;
33884             case 'hbar':
33885                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33886                 break;
33887             case 'pie':
33888 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33889 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33890 //            
33891                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33892                 
33893                 break;
33894
33895         }
33896         
33897         if(this.title){
33898             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33899         }
33900         
33901     },
33902     
33903     setTitle: function(o)
33904     {
33905         this.title = o;
33906     },
33907     
33908     initEvents: function() {
33909         
33910         if(!this.href){
33911             this.el.on('click', this.onClick, this);
33912         }
33913     },
33914     
33915     onClick : function(e)
33916     {
33917         Roo.log('img onclick');
33918         this.fireEvent('click', this, e);
33919     }
33920    
33921 });
33922
33923  
33924 Roo.bootstrap.dash = {};/*
33925  * - LGPL
33926  *
33927  * numberBox
33928  * 
33929  */
33930 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33931
33932 /**
33933  * @class Roo.bootstrap.dash.NumberBox
33934  * @extends Roo.bootstrap.Component
33935  * Bootstrap NumberBox class
33936  * @cfg {String} headline Box headline
33937  * @cfg {String} content Box content
33938  * @cfg {String} icon Box icon
33939  * @cfg {String} footer Footer text
33940  * @cfg {String} fhref Footer href
33941  * 
33942  * @constructor
33943  * Create a new NumberBox
33944  * @param {Object} config The config object
33945  */
33946
33947
33948 Roo.bootstrap.dash.NumberBox = function(config){
33949     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33950     
33951 };
33952
33953 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33954     
33955     headline : '',
33956     content : '',
33957     icon : '',
33958     footer : '',
33959     fhref : '',
33960     ficon : '',
33961     
33962     getAutoCreate : function(){
33963         
33964         var cfg = {
33965             tag : 'div',
33966             cls : 'small-box ',
33967             cn : [
33968                 {
33969                     tag : 'div',
33970                     cls : 'inner',
33971                     cn :[
33972                         {
33973                             tag : 'h3',
33974                             cls : 'roo-headline',
33975                             html : this.headline
33976                         },
33977                         {
33978                             tag : 'p',
33979                             cls : 'roo-content',
33980                             html : this.content
33981                         }
33982                     ]
33983                 }
33984             ]
33985         };
33986         
33987         if(this.icon){
33988             cfg.cn.push({
33989                 tag : 'div',
33990                 cls : 'icon',
33991                 cn :[
33992                     {
33993                         tag : 'i',
33994                         cls : 'ion ' + this.icon
33995                     }
33996                 ]
33997             });
33998         }
33999         
34000         if(this.footer){
34001             var footer = {
34002                 tag : 'a',
34003                 cls : 'small-box-footer',
34004                 href : this.fhref || '#',
34005                 html : this.footer
34006             };
34007             
34008             cfg.cn.push(footer);
34009             
34010         }
34011         
34012         return  cfg;
34013     },
34014
34015     onRender : function(ct,position){
34016         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34017
34018
34019        
34020                 
34021     },
34022
34023     setHeadline: function (value)
34024     {
34025         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34026     },
34027     
34028     setFooter: function (value, href)
34029     {
34030         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34031         
34032         if(href){
34033             this.el.select('a.small-box-footer',true).first().attr('href', href);
34034         }
34035         
34036     },
34037
34038     setContent: function (value)
34039     {
34040         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34041     },
34042
34043     initEvents: function() 
34044     {   
34045         
34046     }
34047     
34048 });
34049
34050  
34051 /*
34052  * - LGPL
34053  *
34054  * TabBox
34055  * 
34056  */
34057 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34058
34059 /**
34060  * @class Roo.bootstrap.dash.TabBox
34061  * @extends Roo.bootstrap.Component
34062  * @children Roo.bootstrap.dash.TabPane
34063  * Bootstrap TabBox class
34064  * @cfg {String} title Title of the TabBox
34065  * @cfg {String} icon Icon of the TabBox
34066  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34067  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34068  * 
34069  * @constructor
34070  * Create a new TabBox
34071  * @param {Object} config The config object
34072  */
34073
34074
34075 Roo.bootstrap.dash.TabBox = function(config){
34076     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34077     this.addEvents({
34078         // raw events
34079         /**
34080          * @event addpane
34081          * When a pane is added
34082          * @param {Roo.bootstrap.dash.TabPane} pane
34083          */
34084         "addpane" : true,
34085         /**
34086          * @event activatepane
34087          * When a pane is activated
34088          * @param {Roo.bootstrap.dash.TabPane} pane
34089          */
34090         "activatepane" : true
34091         
34092          
34093     });
34094     
34095     this.panes = [];
34096 };
34097
34098 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34099
34100     title : '',
34101     icon : false,
34102     showtabs : true,
34103     tabScrollable : false,
34104     
34105     getChildContainer : function()
34106     {
34107         return this.el.select('.tab-content', true).first();
34108     },
34109     
34110     getAutoCreate : function(){
34111         
34112         var header = {
34113             tag: 'li',
34114             cls: 'pull-left header',
34115             html: this.title,
34116             cn : []
34117         };
34118         
34119         if(this.icon){
34120             header.cn.push({
34121                 tag: 'i',
34122                 cls: 'fa ' + this.icon
34123             });
34124         }
34125         
34126         var h = {
34127             tag: 'ul',
34128             cls: 'nav nav-tabs pull-right',
34129             cn: [
34130                 header
34131             ]
34132         };
34133         
34134         if(this.tabScrollable){
34135             h = {
34136                 tag: 'div',
34137                 cls: 'tab-header',
34138                 cn: [
34139                     {
34140                         tag: 'ul',
34141                         cls: 'nav nav-tabs pull-right',
34142                         cn: [
34143                             header
34144                         ]
34145                     }
34146                 ]
34147             };
34148         }
34149         
34150         var cfg = {
34151             tag: 'div',
34152             cls: 'nav-tabs-custom',
34153             cn: [
34154                 h,
34155                 {
34156                     tag: 'div',
34157                     cls: 'tab-content no-padding',
34158                     cn: []
34159                 }
34160             ]
34161         };
34162
34163         return  cfg;
34164     },
34165     initEvents : function()
34166     {
34167         //Roo.log('add add pane handler');
34168         this.on('addpane', this.onAddPane, this);
34169     },
34170      /**
34171      * Updates the box title
34172      * @param {String} html to set the title to.
34173      */
34174     setTitle : function(value)
34175     {
34176         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34177     },
34178     onAddPane : function(pane)
34179     {
34180         this.panes.push(pane);
34181         //Roo.log('addpane');
34182         //Roo.log(pane);
34183         // tabs are rendere left to right..
34184         if(!this.showtabs){
34185             return;
34186         }
34187         
34188         var ctr = this.el.select('.nav-tabs', true).first();
34189          
34190          
34191         var existing = ctr.select('.nav-tab',true);
34192         var qty = existing.getCount();;
34193         
34194         
34195         var tab = ctr.createChild({
34196             tag : 'li',
34197             cls : 'nav-tab' + (qty ? '' : ' active'),
34198             cn : [
34199                 {
34200                     tag : 'a',
34201                     href:'#',
34202                     html : pane.title
34203                 }
34204             ]
34205         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34206         pane.tab = tab;
34207         
34208         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34209         if (!qty) {
34210             pane.el.addClass('active');
34211         }
34212         
34213                 
34214     },
34215     onTabClick : function(ev,un,ob,pane)
34216     {
34217         //Roo.log('tab - prev default');
34218         ev.preventDefault();
34219         
34220         
34221         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34222         pane.tab.addClass('active');
34223         //Roo.log(pane.title);
34224         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34225         // technically we should have a deactivate event.. but maybe add later.
34226         // and it should not de-activate the selected tab...
34227         this.fireEvent('activatepane', pane);
34228         pane.el.addClass('active');
34229         pane.fireEvent('activate');
34230         
34231         
34232     },
34233     
34234     getActivePane : function()
34235     {
34236         var r = false;
34237         Roo.each(this.panes, function(p) {
34238             if(p.el.hasClass('active')){
34239                 r = p;
34240                 return false;
34241             }
34242             
34243             return;
34244         });
34245         
34246         return r;
34247     }
34248     
34249     
34250 });
34251
34252  
34253 /*
34254  * - LGPL
34255  *
34256  * Tab pane
34257  * 
34258  */
34259 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34260 /**
34261  * @class Roo.bootstrap.TabPane
34262  * @extends Roo.bootstrap.Component
34263  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34264  * Bootstrap TabPane class
34265  * @cfg {Boolean} active (false | true) Default false
34266  * @cfg {String} title title of panel
34267
34268  * 
34269  * @constructor
34270  * Create a new TabPane
34271  * @param {Object} config The config object
34272  */
34273
34274 Roo.bootstrap.dash.TabPane = function(config){
34275     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34276     
34277     this.addEvents({
34278         // raw events
34279         /**
34280          * @event activate
34281          * When a pane is activated
34282          * @param {Roo.bootstrap.dash.TabPane} pane
34283          */
34284         "activate" : true
34285          
34286     });
34287 };
34288
34289 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34290     
34291     active : false,
34292     title : '',
34293     
34294     // the tabBox that this is attached to.
34295     tab : false,
34296      
34297     getAutoCreate : function() 
34298     {
34299         var cfg = {
34300             tag: 'div',
34301             cls: 'tab-pane'
34302         };
34303         
34304         if(this.active){
34305             cfg.cls += ' active';
34306         }
34307         
34308         return cfg;
34309     },
34310     initEvents  : function()
34311     {
34312         //Roo.log('trigger add pane handler');
34313         this.parent().fireEvent('addpane', this)
34314     },
34315     
34316      /**
34317      * Updates the tab title 
34318      * @param {String} html to set the title to.
34319      */
34320     setTitle: function(str)
34321     {
34322         if (!this.tab) {
34323             return;
34324         }
34325         this.title = str;
34326         this.tab.select('a', true).first().dom.innerHTML = str;
34327         
34328     }
34329     
34330     
34331     
34332 });
34333
34334  
34335
34336
34337  /*
34338  * - LGPL
34339  *
34340  * Tooltip
34341  * 
34342  */
34343
34344 /**
34345  * @class Roo.bootstrap.Tooltip
34346  * Bootstrap Tooltip class
34347  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34348  * to determine which dom element triggers the tooltip.
34349  * 
34350  * It needs to add support for additional attributes like tooltip-position
34351  * 
34352  * @constructor
34353  * Create a new Toolti
34354  * @param {Object} config The config object
34355  */
34356
34357 Roo.bootstrap.Tooltip = function(config){
34358     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34359     
34360     this.alignment = Roo.bootstrap.Tooltip.alignment;
34361     
34362     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34363         this.alignment = config.alignment;
34364     }
34365     
34366 };
34367
34368 Roo.apply(Roo.bootstrap.Tooltip, {
34369     /**
34370      * @function init initialize tooltip monitoring.
34371      * @static
34372      */
34373     currentEl : false,
34374     currentTip : false,
34375     currentRegion : false,
34376     
34377     //  init : delay?
34378     
34379     init : function()
34380     {
34381         Roo.get(document).on('mouseover', this.enter ,this);
34382         Roo.get(document).on('mouseout', this.leave, this);
34383          
34384         
34385         this.currentTip = new Roo.bootstrap.Tooltip();
34386     },
34387     
34388     enter : function(ev)
34389     {
34390         var dom = ev.getTarget();
34391         
34392         //Roo.log(['enter',dom]);
34393         var el = Roo.fly(dom);
34394         if (this.currentEl) {
34395             //Roo.log(dom);
34396             //Roo.log(this.currentEl);
34397             //Roo.log(this.currentEl.contains(dom));
34398             if (this.currentEl == el) {
34399                 return;
34400             }
34401             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34402                 return;
34403             }
34404
34405         }
34406         
34407         if (this.currentTip.el) {
34408             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34409         }    
34410         //Roo.log(ev);
34411         
34412         if(!el || el.dom == document){
34413             return;
34414         }
34415         
34416         var bindEl = el; 
34417         var pel = false;
34418         if (!el.attr('tooltip')) {
34419             pel = el.findParent("[tooltip]");
34420             if (pel) {
34421                 bindEl = Roo.get(pel);
34422             }
34423         }
34424         
34425        
34426         
34427         // you can not look for children, as if el is the body.. then everythign is the child..
34428         if (!pel && !el.attr('tooltip')) { //
34429             if (!el.select("[tooltip]").elements.length) {
34430                 return;
34431             }
34432             // is the mouse over this child...?
34433             bindEl = el.select("[tooltip]").first();
34434             var xy = ev.getXY();
34435             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34436                 //Roo.log("not in region.");
34437                 return;
34438             }
34439             //Roo.log("child element over..");
34440             
34441         }
34442         this.currentEl = el;
34443         this.currentTip.bind(bindEl);
34444         this.currentRegion = Roo.lib.Region.getRegion(dom);
34445         this.currentTip.enter();
34446         
34447     },
34448     leave : function(ev)
34449     {
34450         var dom = ev.getTarget();
34451         //Roo.log(['leave',dom]);
34452         if (!this.currentEl) {
34453             return;
34454         }
34455         
34456         
34457         if (dom != this.currentEl.dom) {
34458             return;
34459         }
34460         var xy = ev.getXY();
34461         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34462             return;
34463         }
34464         // only activate leave if mouse cursor is outside... bounding box..
34465         
34466         
34467         
34468         
34469         if (this.currentTip) {
34470             this.currentTip.leave();
34471         }
34472         //Roo.log('clear currentEl');
34473         this.currentEl = false;
34474         
34475         
34476     },
34477     alignment : {
34478         'left' : ['r-l', [-2,0], 'right'],
34479         'right' : ['l-r', [2,0], 'left'],
34480         'bottom' : ['t-b', [0,2], 'top'],
34481         'top' : [ 'b-t', [0,-2], 'bottom']
34482     }
34483     
34484 });
34485
34486
34487 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34488     
34489     
34490     bindEl : false,
34491     
34492     delay : null, // can be { show : 300 , hide: 500}
34493     
34494     timeout : null,
34495     
34496     hoverState : null, //???
34497     
34498     placement : 'bottom', 
34499     
34500     alignment : false,
34501     
34502     getAutoCreate : function(){
34503     
34504         var cfg = {
34505            cls : 'tooltip',   
34506            role : 'tooltip',
34507            cn : [
34508                 {
34509                     cls : 'tooltip-arrow arrow'
34510                 },
34511                 {
34512                     cls : 'tooltip-inner'
34513                 }
34514            ]
34515         };
34516         
34517         return cfg;
34518     },
34519     bind : function(el)
34520     {
34521         this.bindEl = el;
34522     },
34523     
34524     initEvents : function()
34525     {
34526         this.arrowEl = this.el.select('.arrow', true).first();
34527         this.innerEl = this.el.select('.tooltip-inner', true).first();
34528     },
34529     
34530     enter : function () {
34531        
34532         if (this.timeout != null) {
34533             clearTimeout(this.timeout);
34534         }
34535         
34536         this.hoverState = 'in';
34537          //Roo.log("enter - show");
34538         if (!this.delay || !this.delay.show) {
34539             this.show();
34540             return;
34541         }
34542         var _t = this;
34543         this.timeout = setTimeout(function () {
34544             if (_t.hoverState == 'in') {
34545                 _t.show();
34546             }
34547         }, this.delay.show);
34548     },
34549     leave : function()
34550     {
34551         clearTimeout(this.timeout);
34552     
34553         this.hoverState = 'out';
34554          if (!this.delay || !this.delay.hide) {
34555             this.hide();
34556             return;
34557         }
34558        
34559         var _t = this;
34560         this.timeout = setTimeout(function () {
34561             //Roo.log("leave - timeout");
34562             
34563             if (_t.hoverState == 'out') {
34564                 _t.hide();
34565                 Roo.bootstrap.Tooltip.currentEl = false;
34566             }
34567         }, delay);
34568     },
34569     
34570     show : function (msg)
34571     {
34572         if (!this.el) {
34573             this.render(document.body);
34574         }
34575         // set content.
34576         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34577         
34578         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34579         
34580         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34581         
34582         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34583                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34584
34585         if(this.bindEl.attr('tooltip-class')) {
34586             this.el.addClass(this.bindEl.attr('tooltip-class'));
34587         }
34588         
34589         var placement = typeof this.placement == 'function' ?
34590             this.placement.call(this, this.el, on_el) :
34591             this.placement;
34592         
34593         if(this.bindEl.attr('tooltip-placement')) {
34594             placement = this.bindEl.attr('tooltip-placement');
34595         }
34596             
34597         var autoToken = /\s?auto?\s?/i;
34598         var autoPlace = autoToken.test(placement);
34599         if (autoPlace) {
34600             placement = placement.replace(autoToken, '') || 'top';
34601         }
34602         
34603         //this.el.detach()
34604         //this.el.setXY([0,0]);
34605         this.el.show();
34606         //this.el.dom.style.display='block';
34607         
34608         //this.el.appendTo(on_el);
34609         
34610         var p = this.getPosition();
34611         var box = this.el.getBox();
34612         
34613         if (autoPlace) {
34614             // fixme..
34615         }
34616         
34617         var align = this.alignment[placement];
34618         
34619         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34620         
34621         if(placement == 'top' || placement == 'bottom'){
34622             if(xy[0] < 0){
34623                 placement = 'right';
34624             }
34625             
34626             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34627                 placement = 'left';
34628             }
34629             
34630             var scroll = Roo.select('body', true).first().getScroll();
34631             
34632             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34633                 placement = 'top';
34634             }
34635             
34636             align = this.alignment[placement];
34637             
34638             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34639             
34640         }
34641         
34642         var elems = document.getElementsByTagName('div');
34643         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34644         for (var i = 0; i < elems.length; i++) {
34645           var zindex = Number.parseInt(
34646                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34647                 10
34648           );
34649           if (zindex > highest) {
34650             highest = zindex;
34651           }
34652         }
34653         
34654         
34655         
34656         this.el.dom.style.zIndex = highest;
34657         
34658         this.el.alignTo(this.bindEl, align[0],align[1]);
34659         //var arrow = this.el.select('.arrow',true).first();
34660         //arrow.set(align[2], 
34661         
34662         this.el.addClass(placement);
34663         this.el.addClass("bs-tooltip-"+ placement);
34664         
34665         this.el.addClass('in fade show');
34666         
34667         this.hoverState = null;
34668         
34669         if (this.el.hasClass('fade')) {
34670             // fade it?
34671         }
34672         
34673         
34674         
34675         
34676         
34677     },
34678     hide : function()
34679     {
34680          
34681         if (!this.el) {
34682             return;
34683         }
34684         //this.el.setXY([0,0]);
34685         if(this.bindEl.attr('tooltip-class')) {
34686             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34687         }
34688         this.el.removeClass(['show', 'in']);
34689         //this.el.hide();
34690         
34691     }
34692     
34693 });
34694  
34695
34696  /*
34697  * - LGPL
34698  *
34699  * Location Picker
34700  * 
34701  */
34702
34703 /**
34704  * @class Roo.bootstrap.LocationPicker
34705  * @extends Roo.bootstrap.Component
34706  * Bootstrap LocationPicker class
34707  * @cfg {Number} latitude Position when init default 0
34708  * @cfg {Number} longitude Position when init default 0
34709  * @cfg {Number} zoom default 15
34710  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34711  * @cfg {Boolean} mapTypeControl default false
34712  * @cfg {Boolean} disableDoubleClickZoom default false
34713  * @cfg {Boolean} scrollwheel default true
34714  * @cfg {Boolean} streetViewControl default false
34715  * @cfg {Number} radius default 0
34716  * @cfg {String} locationName
34717  * @cfg {Boolean} draggable default true
34718  * @cfg {Boolean} enableAutocomplete default false
34719  * @cfg {Boolean} enableReverseGeocode default true
34720  * @cfg {String} markerTitle
34721  * 
34722  * @constructor
34723  * Create a new LocationPicker
34724  * @param {Object} config The config object
34725  */
34726
34727
34728 Roo.bootstrap.LocationPicker = function(config){
34729     
34730     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34731     
34732     this.addEvents({
34733         /**
34734          * @event initial
34735          * Fires when the picker initialized.
34736          * @param {Roo.bootstrap.LocationPicker} this
34737          * @param {Google Location} location
34738          */
34739         initial : true,
34740         /**
34741          * @event positionchanged
34742          * Fires when the picker position changed.
34743          * @param {Roo.bootstrap.LocationPicker} this
34744          * @param {Google Location} location
34745          */
34746         positionchanged : true,
34747         /**
34748          * @event resize
34749          * Fires when the map resize.
34750          * @param {Roo.bootstrap.LocationPicker} this
34751          */
34752         resize : true,
34753         /**
34754          * @event show
34755          * Fires when the map show.
34756          * @param {Roo.bootstrap.LocationPicker} this
34757          */
34758         show : true,
34759         /**
34760          * @event hide
34761          * Fires when the map hide.
34762          * @param {Roo.bootstrap.LocationPicker} this
34763          */
34764         hide : true,
34765         /**
34766          * @event mapClick
34767          * Fires when click the map.
34768          * @param {Roo.bootstrap.LocationPicker} this
34769          * @param {Map event} e
34770          */
34771         mapClick : true,
34772         /**
34773          * @event mapRightClick
34774          * Fires when right click the map.
34775          * @param {Roo.bootstrap.LocationPicker} this
34776          * @param {Map event} e
34777          */
34778         mapRightClick : true,
34779         /**
34780          * @event markerClick
34781          * Fires when click the marker.
34782          * @param {Roo.bootstrap.LocationPicker} this
34783          * @param {Map event} e
34784          */
34785         markerClick : true,
34786         /**
34787          * @event markerRightClick
34788          * Fires when right click the marker.
34789          * @param {Roo.bootstrap.LocationPicker} this
34790          * @param {Map event} e
34791          */
34792         markerRightClick : true,
34793         /**
34794          * @event OverlayViewDraw
34795          * Fires when OverlayView Draw
34796          * @param {Roo.bootstrap.LocationPicker} this
34797          */
34798         OverlayViewDraw : true,
34799         /**
34800          * @event OverlayViewOnAdd
34801          * Fires when OverlayView Draw
34802          * @param {Roo.bootstrap.LocationPicker} this
34803          */
34804         OverlayViewOnAdd : true,
34805         /**
34806          * @event OverlayViewOnRemove
34807          * Fires when OverlayView Draw
34808          * @param {Roo.bootstrap.LocationPicker} this
34809          */
34810         OverlayViewOnRemove : true,
34811         /**
34812          * @event OverlayViewShow
34813          * Fires when OverlayView Draw
34814          * @param {Roo.bootstrap.LocationPicker} this
34815          * @param {Pixel} cpx
34816          */
34817         OverlayViewShow : true,
34818         /**
34819          * @event OverlayViewHide
34820          * Fires when OverlayView Draw
34821          * @param {Roo.bootstrap.LocationPicker} this
34822          */
34823         OverlayViewHide : true,
34824         /**
34825          * @event loadexception
34826          * Fires when load google lib failed.
34827          * @param {Roo.bootstrap.LocationPicker} this
34828          */
34829         loadexception : true
34830     });
34831         
34832 };
34833
34834 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34835     
34836     gMapContext: false,
34837     
34838     latitude: 0,
34839     longitude: 0,
34840     zoom: 15,
34841     mapTypeId: false,
34842     mapTypeControl: false,
34843     disableDoubleClickZoom: false,
34844     scrollwheel: true,
34845     streetViewControl: false,
34846     radius: 0,
34847     locationName: '',
34848     draggable: true,
34849     enableAutocomplete: false,
34850     enableReverseGeocode: true,
34851     markerTitle: '',
34852     
34853     getAutoCreate: function()
34854     {
34855
34856         var cfg = {
34857             tag: 'div',
34858             cls: 'roo-location-picker'
34859         };
34860         
34861         return cfg
34862     },
34863     
34864     initEvents: function(ct, position)
34865     {       
34866         if(!this.el.getWidth() || this.isApplied()){
34867             return;
34868         }
34869         
34870         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34871         
34872         this.initial();
34873     },
34874     
34875     initial: function()
34876     {
34877         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34878             this.fireEvent('loadexception', this);
34879             return;
34880         }
34881         
34882         if(!this.mapTypeId){
34883             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34884         }
34885         
34886         this.gMapContext = this.GMapContext();
34887         
34888         this.initOverlayView();
34889         
34890         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34891         
34892         var _this = this;
34893                 
34894         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34895             _this.setPosition(_this.gMapContext.marker.position);
34896         });
34897         
34898         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34899             _this.fireEvent('mapClick', this, event);
34900             
34901         });
34902
34903         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34904             _this.fireEvent('mapRightClick', this, event);
34905             
34906         });
34907         
34908         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34909             _this.fireEvent('markerClick', this, event);
34910             
34911         });
34912
34913         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34914             _this.fireEvent('markerRightClick', this, event);
34915             
34916         });
34917         
34918         this.setPosition(this.gMapContext.location);
34919         
34920         this.fireEvent('initial', this, this.gMapContext.location);
34921     },
34922     
34923     initOverlayView: function()
34924     {
34925         var _this = this;
34926         
34927         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34928             
34929             draw: function()
34930             {
34931                 _this.fireEvent('OverlayViewDraw', _this);
34932             },
34933             
34934             onAdd: function()
34935             {
34936                 _this.fireEvent('OverlayViewOnAdd', _this);
34937             },
34938             
34939             onRemove: function()
34940             {
34941                 _this.fireEvent('OverlayViewOnRemove', _this);
34942             },
34943             
34944             show: function(cpx)
34945             {
34946                 _this.fireEvent('OverlayViewShow', _this, cpx);
34947             },
34948             
34949             hide: function()
34950             {
34951                 _this.fireEvent('OverlayViewHide', _this);
34952             }
34953             
34954         });
34955     },
34956     
34957     fromLatLngToContainerPixel: function(event)
34958     {
34959         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34960     },
34961     
34962     isApplied: function() 
34963     {
34964         return this.getGmapContext() == false ? false : true;
34965     },
34966     
34967     getGmapContext: function() 
34968     {
34969         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34970     },
34971     
34972     GMapContext: function() 
34973     {
34974         var position = new google.maps.LatLng(this.latitude, this.longitude);
34975         
34976         var _map = new google.maps.Map(this.el.dom, {
34977             center: position,
34978             zoom: this.zoom,
34979             mapTypeId: this.mapTypeId,
34980             mapTypeControl: this.mapTypeControl,
34981             disableDoubleClickZoom: this.disableDoubleClickZoom,
34982             scrollwheel: this.scrollwheel,
34983             streetViewControl: this.streetViewControl,
34984             locationName: this.locationName,
34985             draggable: this.draggable,
34986             enableAutocomplete: this.enableAutocomplete,
34987             enableReverseGeocode: this.enableReverseGeocode
34988         });
34989         
34990         var _marker = new google.maps.Marker({
34991             position: position,
34992             map: _map,
34993             title: this.markerTitle,
34994             draggable: this.draggable
34995         });
34996         
34997         return {
34998             map: _map,
34999             marker: _marker,
35000             circle: null,
35001             location: position,
35002             radius: this.radius,
35003             locationName: this.locationName,
35004             addressComponents: {
35005                 formatted_address: null,
35006                 addressLine1: null,
35007                 addressLine2: null,
35008                 streetName: null,
35009                 streetNumber: null,
35010                 city: null,
35011                 district: null,
35012                 state: null,
35013                 stateOrProvince: null
35014             },
35015             settings: this,
35016             domContainer: this.el.dom,
35017             geodecoder: new google.maps.Geocoder()
35018         };
35019     },
35020     
35021     drawCircle: function(center, radius, options) 
35022     {
35023         if (this.gMapContext.circle != null) {
35024             this.gMapContext.circle.setMap(null);
35025         }
35026         if (radius > 0) {
35027             radius *= 1;
35028             options = Roo.apply({}, options, {
35029                 strokeColor: "#0000FF",
35030                 strokeOpacity: .35,
35031                 strokeWeight: 2,
35032                 fillColor: "#0000FF",
35033                 fillOpacity: .2
35034             });
35035             
35036             options.map = this.gMapContext.map;
35037             options.radius = radius;
35038             options.center = center;
35039             this.gMapContext.circle = new google.maps.Circle(options);
35040             return this.gMapContext.circle;
35041         }
35042         
35043         return null;
35044     },
35045     
35046     setPosition: function(location) 
35047     {
35048         this.gMapContext.location = location;
35049         this.gMapContext.marker.setPosition(location);
35050         this.gMapContext.map.panTo(location);
35051         this.drawCircle(location, this.gMapContext.radius, {});
35052         
35053         var _this = this;
35054         
35055         if (this.gMapContext.settings.enableReverseGeocode) {
35056             this.gMapContext.geodecoder.geocode({
35057                 latLng: this.gMapContext.location
35058             }, function(results, status) {
35059                 
35060                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35061                     _this.gMapContext.locationName = results[0].formatted_address;
35062                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35063                     
35064                     _this.fireEvent('positionchanged', this, location);
35065                 }
35066             });
35067             
35068             return;
35069         }
35070         
35071         this.fireEvent('positionchanged', this, location);
35072     },
35073     
35074     resize: function()
35075     {
35076         google.maps.event.trigger(this.gMapContext.map, "resize");
35077         
35078         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35079         
35080         this.fireEvent('resize', this);
35081     },
35082     
35083     setPositionByLatLng: function(latitude, longitude)
35084     {
35085         this.setPosition(new google.maps.LatLng(latitude, longitude));
35086     },
35087     
35088     getCurrentPosition: function() 
35089     {
35090         return {
35091             latitude: this.gMapContext.location.lat(),
35092             longitude: this.gMapContext.location.lng()
35093         };
35094     },
35095     
35096     getAddressName: function() 
35097     {
35098         return this.gMapContext.locationName;
35099     },
35100     
35101     getAddressComponents: function() 
35102     {
35103         return this.gMapContext.addressComponents;
35104     },
35105     
35106     address_component_from_google_geocode: function(address_components) 
35107     {
35108         var result = {};
35109         
35110         for (var i = 0; i < address_components.length; i++) {
35111             var component = address_components[i];
35112             if (component.types.indexOf("postal_code") >= 0) {
35113                 result.postalCode = component.short_name;
35114             } else if (component.types.indexOf("street_number") >= 0) {
35115                 result.streetNumber = component.short_name;
35116             } else if (component.types.indexOf("route") >= 0) {
35117                 result.streetName = component.short_name;
35118             } else if (component.types.indexOf("neighborhood") >= 0) {
35119                 result.city = component.short_name;
35120             } else if (component.types.indexOf("locality") >= 0) {
35121                 result.city = component.short_name;
35122             } else if (component.types.indexOf("sublocality") >= 0) {
35123                 result.district = component.short_name;
35124             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35125                 result.stateOrProvince = component.short_name;
35126             } else if (component.types.indexOf("country") >= 0) {
35127                 result.country = component.short_name;
35128             }
35129         }
35130         
35131         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35132         result.addressLine2 = "";
35133         return result;
35134     },
35135     
35136     setZoomLevel: function(zoom)
35137     {
35138         this.gMapContext.map.setZoom(zoom);
35139     },
35140     
35141     show: function()
35142     {
35143         if(!this.el){
35144             return;
35145         }
35146         
35147         this.el.show();
35148         
35149         this.resize();
35150         
35151         this.fireEvent('show', this);
35152     },
35153     
35154     hide: function()
35155     {
35156         if(!this.el){
35157             return;
35158         }
35159         
35160         this.el.hide();
35161         
35162         this.fireEvent('hide', this);
35163     }
35164     
35165 });
35166
35167 Roo.apply(Roo.bootstrap.LocationPicker, {
35168     
35169     OverlayView : function(map, options)
35170     {
35171         options = options || {};
35172         
35173         this.setMap(map);
35174     }
35175     
35176     
35177 });/**
35178  * @class Roo.bootstrap.Alert
35179  * @extends Roo.bootstrap.Component
35180  * Bootstrap Alert class - shows an alert area box
35181  * eg
35182  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35183   Enter a valid email address
35184 </div>
35185  * @licence LGPL
35186  * @cfg {String} title The title of alert
35187  * @cfg {String} html The content of alert
35188  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35189  * @cfg {String} fa font-awesomeicon
35190  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35191  * @cfg {Boolean} close true to show a x closer
35192  * 
35193  * 
35194  * @constructor
35195  * Create a new alert
35196  * @param {Object} config The config object
35197  */
35198
35199
35200 Roo.bootstrap.Alert = function(config){
35201     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35202     
35203 };
35204
35205 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35206     
35207     title: '',
35208     html: '',
35209     weight: false,
35210     fa: false,
35211     faicon: false, // BC
35212     close : false,
35213     
35214     
35215     getAutoCreate : function()
35216     {
35217         
35218         var cfg = {
35219             tag : 'div',
35220             cls : 'alert',
35221             cn : [
35222                 {
35223                     tag: 'button',
35224                     type :  "button",
35225                     cls: "close",
35226                     html : '×',
35227                     style : this.close ? '' : 'display:none'
35228                 },
35229                 {
35230                     tag : 'i',
35231                     cls : 'roo-alert-icon'
35232                     
35233                 },
35234                 {
35235                     tag : 'b',
35236                     cls : 'roo-alert-title',
35237                     html : this.title
35238                 },
35239                 {
35240                     tag : 'span',
35241                     cls : 'roo-alert-text',
35242                     html : this.html
35243                 }
35244             ]
35245         };
35246         
35247         if(this.faicon){
35248             cfg.cn[0].cls += ' fa ' + this.faicon;
35249         }
35250         if(this.fa){
35251             cfg.cn[0].cls += ' fa ' + this.fa;
35252         }
35253         
35254         if(this.weight){
35255             cfg.cls += ' alert-' + this.weight;
35256         }
35257         
35258         return cfg;
35259     },
35260     
35261     initEvents: function() 
35262     {
35263         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35264         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35265         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35266         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35267         if (this.seconds > 0) {
35268             this.hide.defer(this.seconds, this);
35269         }
35270     },
35271     /**
35272      * Set the Title Message HTML
35273      * @param {String} html
35274      */
35275     setTitle : function(str)
35276     {
35277         this.titleEl.dom.innerHTML = str;
35278     },
35279      
35280      /**
35281      * Set the Body Message HTML
35282      * @param {String} html
35283      */
35284     setHtml : function(str)
35285     {
35286         this.htmlEl.dom.innerHTML = str;
35287     },
35288     /**
35289      * Set the Weight of the alert
35290      * @param {String} (success|info|warning|danger) weight
35291      */
35292     
35293     setWeight : function(weight)
35294     {
35295         if(this.weight){
35296             this.el.removeClass('alert-' + this.weight);
35297         }
35298         
35299         this.weight = weight;
35300         
35301         this.el.addClass('alert-' + this.weight);
35302     },
35303       /**
35304      * Set the Icon of the alert
35305      * @param {String} see fontawsome names (name without the 'fa-' bit)
35306      */
35307     setIcon : function(icon)
35308     {
35309         if(this.faicon){
35310             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35311         }
35312         
35313         this.faicon = icon;
35314         
35315         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35316     },
35317     /**
35318      * Hide the Alert
35319      */
35320     hide: function() 
35321     {
35322         this.el.hide();   
35323     },
35324     /**
35325      * Show the Alert
35326      */
35327     show: function() 
35328     {  
35329         this.el.show();   
35330     }
35331     
35332 });
35333
35334  
35335 /*
35336 * Licence: LGPL
35337 */
35338
35339 /**
35340  * @class Roo.bootstrap.UploadCropbox
35341  * @extends Roo.bootstrap.Component
35342  * Bootstrap UploadCropbox class
35343  * @cfg {String} emptyText show when image has been loaded
35344  * @cfg {String} rotateNotify show when image too small to rotate
35345  * @cfg {Number} errorTimeout default 3000
35346  * @cfg {Number} minWidth default 300
35347  * @cfg {Number} minHeight default 300
35348  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35349  * @cfg {Boolean} isDocument (true|false) default false
35350  * @cfg {String} url action url
35351  * @cfg {String} paramName default 'imageUpload'
35352  * @cfg {String} method default POST
35353  * @cfg {Boolean} loadMask (true|false) default true
35354  * @cfg {Boolean} loadingText default 'Loading...'
35355  * 
35356  * @constructor
35357  * Create a new UploadCropbox
35358  * @param {Object} config The config object
35359  */
35360
35361 Roo.bootstrap.UploadCropbox = function(config){
35362     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35363     
35364     this.addEvents({
35365         /**
35366          * @event beforeselectfile
35367          * Fire before select file
35368          * @param {Roo.bootstrap.UploadCropbox} this
35369          */
35370         "beforeselectfile" : true,
35371         /**
35372          * @event initial
35373          * Fire after initEvent
35374          * @param {Roo.bootstrap.UploadCropbox} this
35375          */
35376         "initial" : true,
35377         /**
35378          * @event crop
35379          * Fire after initEvent
35380          * @param {Roo.bootstrap.UploadCropbox} this
35381          * @param {String} data
35382          */
35383         "crop" : true,
35384         /**
35385          * @event prepare
35386          * Fire when preparing the file data
35387          * @param {Roo.bootstrap.UploadCropbox} this
35388          * @param {Object} file
35389          */
35390         "prepare" : true,
35391         /**
35392          * @event exception
35393          * Fire when get exception
35394          * @param {Roo.bootstrap.UploadCropbox} this
35395          * @param {XMLHttpRequest} xhr
35396          */
35397         "exception" : true,
35398         /**
35399          * @event beforeloadcanvas
35400          * Fire before load the canvas
35401          * @param {Roo.bootstrap.UploadCropbox} this
35402          * @param {String} src
35403          */
35404         "beforeloadcanvas" : true,
35405         /**
35406          * @event trash
35407          * Fire when trash image
35408          * @param {Roo.bootstrap.UploadCropbox} this
35409          */
35410         "trash" : true,
35411         /**
35412          * @event download
35413          * Fire when download the image
35414          * @param {Roo.bootstrap.UploadCropbox} this
35415          */
35416         "download" : true,
35417         /**
35418          * @event footerbuttonclick
35419          * Fire when footerbuttonclick
35420          * @param {Roo.bootstrap.UploadCropbox} this
35421          * @param {String} type
35422          */
35423         "footerbuttonclick" : true,
35424         /**
35425          * @event resize
35426          * Fire when resize
35427          * @param {Roo.bootstrap.UploadCropbox} this
35428          */
35429         "resize" : true,
35430         /**
35431          * @event rotate
35432          * Fire when rotate the image
35433          * @param {Roo.bootstrap.UploadCropbox} this
35434          * @param {String} pos
35435          */
35436         "rotate" : true,
35437         /**
35438          * @event inspect
35439          * Fire when inspect the file
35440          * @param {Roo.bootstrap.UploadCropbox} this
35441          * @param {Object} file
35442          */
35443         "inspect" : true,
35444         /**
35445          * @event upload
35446          * Fire when xhr upload the file
35447          * @param {Roo.bootstrap.UploadCropbox} this
35448          * @param {Object} data
35449          */
35450         "upload" : true,
35451         /**
35452          * @event arrange
35453          * Fire when arrange the file data
35454          * @param {Roo.bootstrap.UploadCropbox} this
35455          * @param {Object} formData
35456          */
35457         "arrange" : true
35458     });
35459     
35460     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35461 };
35462
35463 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35464     
35465     emptyText : 'Click to upload image',
35466     rotateNotify : 'Image is too small to rotate',
35467     errorTimeout : 3000,
35468     scale : 0,
35469     baseScale : 1,
35470     rotate : 0,
35471     dragable : false,
35472     pinching : false,
35473     mouseX : 0,
35474     mouseY : 0,
35475     cropData : false,
35476     minWidth : 300,
35477     minHeight : 300,
35478     file : false,
35479     exif : {},
35480     baseRotate : 1,
35481     cropType : 'image/jpeg',
35482     buttons : false,
35483     canvasLoaded : false,
35484     isDocument : false,
35485     method : 'POST',
35486     paramName : 'imageUpload',
35487     loadMask : true,
35488     loadingText : 'Loading...',
35489     maskEl : false,
35490     
35491     getAutoCreate : function()
35492     {
35493         var cfg = {
35494             tag : 'div',
35495             cls : 'roo-upload-cropbox',
35496             cn : [
35497                 {
35498                     tag : 'input',
35499                     cls : 'roo-upload-cropbox-selector',
35500                     type : 'file'
35501                 },
35502                 {
35503                     tag : 'div',
35504                     cls : 'roo-upload-cropbox-body',
35505                     style : 'cursor:pointer',
35506                     cn : [
35507                         {
35508                             tag : 'div',
35509                             cls : 'roo-upload-cropbox-preview'
35510                         },
35511                         {
35512                             tag : 'div',
35513                             cls : 'roo-upload-cropbox-thumb'
35514                         },
35515                         {
35516                             tag : 'div',
35517                             cls : 'roo-upload-cropbox-empty-notify',
35518                             html : this.emptyText
35519                         },
35520                         {
35521                             tag : 'div',
35522                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35523                             html : this.rotateNotify
35524                         }
35525                     ]
35526                 },
35527                 {
35528                     tag : 'div',
35529                     cls : 'roo-upload-cropbox-footer',
35530                     cn : {
35531                         tag : 'div',
35532                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35533                         cn : []
35534                     }
35535                 }
35536             ]
35537         };
35538         
35539         return cfg;
35540     },
35541     
35542     onRender : function(ct, position)
35543     {
35544         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35545         
35546         if (this.buttons.length) {
35547             
35548             Roo.each(this.buttons, function(bb) {
35549                 
35550                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35551                 
35552                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35553                 
35554             }, this);
35555         }
35556         
35557         if(this.loadMask){
35558             this.maskEl = this.el;
35559         }
35560     },
35561     
35562     initEvents : function()
35563     {
35564         this.urlAPI = (window.createObjectURL && window) || 
35565                                 (window.URL && URL.revokeObjectURL && URL) || 
35566                                 (window.webkitURL && webkitURL);
35567                         
35568         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35569         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35570         
35571         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35572         this.selectorEl.hide();
35573         
35574         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35575         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35576         
35577         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35578         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35579         this.thumbEl.hide();
35580         
35581         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35582         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35583         
35584         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35585         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35586         this.errorEl.hide();
35587         
35588         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35589         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35590         this.footerEl.hide();
35591         
35592         this.setThumbBoxSize();
35593         
35594         this.bind();
35595         
35596         this.resize();
35597         
35598         this.fireEvent('initial', this);
35599     },
35600
35601     bind : function()
35602     {
35603         var _this = this;
35604         
35605         window.addEventListener("resize", function() { _this.resize(); } );
35606         
35607         this.bodyEl.on('click', this.beforeSelectFile, this);
35608         
35609         if(Roo.isTouch){
35610             this.bodyEl.on('touchstart', this.onTouchStart, this);
35611             this.bodyEl.on('touchmove', this.onTouchMove, this);
35612             this.bodyEl.on('touchend', this.onTouchEnd, this);
35613         }
35614         
35615         if(!Roo.isTouch){
35616             this.bodyEl.on('mousedown', this.onMouseDown, this);
35617             this.bodyEl.on('mousemove', this.onMouseMove, this);
35618             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35619             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35620             Roo.get(document).on('mouseup', this.onMouseUp, this);
35621         }
35622         
35623         this.selectorEl.on('change', this.onFileSelected, this);
35624     },
35625     
35626     reset : function()
35627     {    
35628         this.scale = 0;
35629         this.baseScale = 1;
35630         this.rotate = 0;
35631         this.baseRotate = 1;
35632         this.dragable = false;
35633         this.pinching = false;
35634         this.mouseX = 0;
35635         this.mouseY = 0;
35636         this.cropData = false;
35637         this.notifyEl.dom.innerHTML = this.emptyText;
35638         
35639         this.selectorEl.dom.value = '';
35640         
35641     },
35642     
35643     resize : function()
35644     {
35645         if(this.fireEvent('resize', this) != false){
35646             this.setThumbBoxPosition();
35647             this.setCanvasPosition();
35648         }
35649     },
35650     
35651     onFooterButtonClick : function(e, el, o, type)
35652     {
35653         switch (type) {
35654             case 'rotate-left' :
35655                 this.onRotateLeft(e);
35656                 break;
35657             case 'rotate-right' :
35658                 this.onRotateRight(e);
35659                 break;
35660             case 'picture' :
35661                 this.beforeSelectFile(e);
35662                 break;
35663             case 'trash' :
35664                 this.trash(e);
35665                 break;
35666             case 'crop' :
35667                 this.crop(e);
35668                 break;
35669             case 'download' :
35670                 this.download(e);
35671                 break;
35672             default :
35673                 break;
35674         }
35675         
35676         this.fireEvent('footerbuttonclick', this, type);
35677     },
35678     
35679     beforeSelectFile : function(e)
35680     {
35681         e.preventDefault();
35682         
35683         if(this.fireEvent('beforeselectfile', this) != false){
35684             this.selectorEl.dom.click();
35685         }
35686     },
35687     
35688     onFileSelected : function(e)
35689     {
35690         e.preventDefault();
35691         
35692         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35693             return;
35694         }
35695         
35696         var file = this.selectorEl.dom.files[0];
35697         
35698         if(this.fireEvent('inspect', this, file) != false){
35699             this.prepare(file);
35700         }
35701         
35702     },
35703     
35704     trash : function(e)
35705     {
35706         this.fireEvent('trash', this);
35707     },
35708     
35709     download : function(e)
35710     {
35711         this.fireEvent('download', this);
35712     },
35713     
35714     loadCanvas : function(src)
35715     {   
35716         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35717             
35718             this.reset();
35719             
35720             this.imageEl = document.createElement('img');
35721             
35722             var _this = this;
35723             
35724             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35725             
35726             this.imageEl.src = src;
35727         }
35728     },
35729     
35730     onLoadCanvas : function()
35731     {   
35732         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35733         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35734         
35735         this.bodyEl.un('click', this.beforeSelectFile, this);
35736         
35737         this.notifyEl.hide();
35738         this.thumbEl.show();
35739         this.footerEl.show();
35740         
35741         this.baseRotateLevel();
35742         
35743         if(this.isDocument){
35744             this.setThumbBoxSize();
35745         }
35746         
35747         this.setThumbBoxPosition();
35748         
35749         this.baseScaleLevel();
35750         
35751         this.draw();
35752         
35753         this.resize();
35754         
35755         this.canvasLoaded = true;
35756         
35757         if(this.loadMask){
35758             this.maskEl.unmask();
35759         }
35760         
35761     },
35762     
35763     setCanvasPosition : function()
35764     {   
35765         if(!this.canvasEl){
35766             return;
35767         }
35768         
35769         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35770         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35771         
35772         this.previewEl.setLeft(pw);
35773         this.previewEl.setTop(ph);
35774         
35775     },
35776     
35777     onMouseDown : function(e)
35778     {   
35779         e.stopEvent();
35780         
35781         this.dragable = true;
35782         this.pinching = false;
35783         
35784         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35785             this.dragable = false;
35786             return;
35787         }
35788         
35789         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35790         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35791         
35792     },
35793     
35794     onMouseMove : function(e)
35795     {   
35796         e.stopEvent();
35797         
35798         if(!this.canvasLoaded){
35799             return;
35800         }
35801         
35802         if (!this.dragable){
35803             return;
35804         }
35805         
35806         var minX = Math.ceil(this.thumbEl.getLeft(true));
35807         var minY = Math.ceil(this.thumbEl.getTop(true));
35808         
35809         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35810         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35811         
35812         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35813         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35814         
35815         x = x - this.mouseX;
35816         y = y - this.mouseY;
35817         
35818         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35819         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35820         
35821         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35822         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35823         
35824         this.previewEl.setLeft(bgX);
35825         this.previewEl.setTop(bgY);
35826         
35827         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35828         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35829     },
35830     
35831     onMouseUp : function(e)
35832     {   
35833         e.stopEvent();
35834         
35835         this.dragable = false;
35836     },
35837     
35838     onMouseWheel : function(e)
35839     {   
35840         e.stopEvent();
35841         
35842         this.startScale = this.scale;
35843         
35844         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35845         
35846         if(!this.zoomable()){
35847             this.scale = this.startScale;
35848             return;
35849         }
35850         
35851         this.draw();
35852         
35853         return;
35854     },
35855     
35856     zoomable : function()
35857     {
35858         var minScale = this.thumbEl.getWidth() / this.minWidth;
35859         
35860         if(this.minWidth < this.minHeight){
35861             minScale = this.thumbEl.getHeight() / this.minHeight;
35862         }
35863         
35864         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35865         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35866         
35867         if(
35868                 this.isDocument &&
35869                 (this.rotate == 0 || this.rotate == 180) && 
35870                 (
35871                     width > this.imageEl.OriginWidth || 
35872                     height > this.imageEl.OriginHeight ||
35873                     (width < this.minWidth && height < this.minHeight)
35874                 )
35875         ){
35876             return false;
35877         }
35878         
35879         if(
35880                 this.isDocument &&
35881                 (this.rotate == 90 || this.rotate == 270) && 
35882                 (
35883                     width > this.imageEl.OriginWidth || 
35884                     height > this.imageEl.OriginHeight ||
35885                     (width < this.minHeight && height < this.minWidth)
35886                 )
35887         ){
35888             return false;
35889         }
35890         
35891         if(
35892                 !this.isDocument &&
35893                 (this.rotate == 0 || this.rotate == 180) && 
35894                 (
35895                     width < this.minWidth || 
35896                     width > this.imageEl.OriginWidth || 
35897                     height < this.minHeight || 
35898                     height > this.imageEl.OriginHeight
35899                 )
35900         ){
35901             return false;
35902         }
35903         
35904         if(
35905                 !this.isDocument &&
35906                 (this.rotate == 90 || this.rotate == 270) && 
35907                 (
35908                     width < this.minHeight || 
35909                     width > this.imageEl.OriginWidth || 
35910                     height < this.minWidth || 
35911                     height > this.imageEl.OriginHeight
35912                 )
35913         ){
35914             return false;
35915         }
35916         
35917         return true;
35918         
35919     },
35920     
35921     onRotateLeft : function(e)
35922     {   
35923         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35924             
35925             var minScale = this.thumbEl.getWidth() / this.minWidth;
35926             
35927             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35928             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35929             
35930             this.startScale = this.scale;
35931             
35932             while (this.getScaleLevel() < minScale){
35933             
35934                 this.scale = this.scale + 1;
35935                 
35936                 if(!this.zoomable()){
35937                     break;
35938                 }
35939                 
35940                 if(
35941                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35942                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35943                 ){
35944                     continue;
35945                 }
35946                 
35947                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35948
35949                 this.draw();
35950                 
35951                 return;
35952             }
35953             
35954             this.scale = this.startScale;
35955             
35956             this.onRotateFail();
35957             
35958             return false;
35959         }
35960         
35961         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35962
35963         if(this.isDocument){
35964             this.setThumbBoxSize();
35965             this.setThumbBoxPosition();
35966             this.setCanvasPosition();
35967         }
35968         
35969         this.draw();
35970         
35971         this.fireEvent('rotate', this, 'left');
35972         
35973     },
35974     
35975     onRotateRight : function(e)
35976     {
35977         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35978             
35979             var minScale = this.thumbEl.getWidth() / this.minWidth;
35980         
35981             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35982             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35983             
35984             this.startScale = this.scale;
35985             
35986             while (this.getScaleLevel() < minScale){
35987             
35988                 this.scale = this.scale + 1;
35989                 
35990                 if(!this.zoomable()){
35991                     break;
35992                 }
35993                 
35994                 if(
35995                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35996                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35997                 ){
35998                     continue;
35999                 }
36000                 
36001                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36002
36003                 this.draw();
36004                 
36005                 return;
36006             }
36007             
36008             this.scale = this.startScale;
36009             
36010             this.onRotateFail();
36011             
36012             return false;
36013         }
36014         
36015         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36016
36017         if(this.isDocument){
36018             this.setThumbBoxSize();
36019             this.setThumbBoxPosition();
36020             this.setCanvasPosition();
36021         }
36022         
36023         this.draw();
36024         
36025         this.fireEvent('rotate', this, 'right');
36026     },
36027     
36028     onRotateFail : function()
36029     {
36030         this.errorEl.show(true);
36031         
36032         var _this = this;
36033         
36034         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36035     },
36036     
36037     draw : function()
36038     {
36039         this.previewEl.dom.innerHTML = '';
36040         
36041         var canvasEl = document.createElement("canvas");
36042         
36043         var contextEl = canvasEl.getContext("2d");
36044         
36045         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36046         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36047         var center = this.imageEl.OriginWidth / 2;
36048         
36049         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36050             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36051             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36052             center = this.imageEl.OriginHeight / 2;
36053         }
36054         
36055         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36056         
36057         contextEl.translate(center, center);
36058         contextEl.rotate(this.rotate * Math.PI / 180);
36059
36060         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36061         
36062         this.canvasEl = document.createElement("canvas");
36063         
36064         this.contextEl = this.canvasEl.getContext("2d");
36065         
36066         switch (this.rotate) {
36067             case 0 :
36068                 
36069                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36070                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36071                 
36072                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36073                 
36074                 break;
36075             case 90 : 
36076                 
36077                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36078                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36079                 
36080                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36081                     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);
36082                     break;
36083                 }
36084                 
36085                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36086                 
36087                 break;
36088             case 180 :
36089                 
36090                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36091                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36092                 
36093                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36094                     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);
36095                     break;
36096                 }
36097                 
36098                 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);
36099                 
36100                 break;
36101             case 270 :
36102                 
36103                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36104                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36105         
36106                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36107                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36108                     break;
36109                 }
36110                 
36111                 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);
36112                 
36113                 break;
36114             default : 
36115                 break;
36116         }
36117         
36118         this.previewEl.appendChild(this.canvasEl);
36119         
36120         this.setCanvasPosition();
36121     },
36122     
36123     crop : function()
36124     {
36125         if(!this.canvasLoaded){
36126             return;
36127         }
36128         
36129         var imageCanvas = document.createElement("canvas");
36130         
36131         var imageContext = imageCanvas.getContext("2d");
36132         
36133         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36134         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36135         
36136         var center = imageCanvas.width / 2;
36137         
36138         imageContext.translate(center, center);
36139         
36140         imageContext.rotate(this.rotate * Math.PI / 180);
36141         
36142         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36143         
36144         var canvas = document.createElement("canvas");
36145         
36146         var context = canvas.getContext("2d");
36147                 
36148         canvas.width = this.minWidth;
36149         canvas.height = this.minHeight;
36150
36151         switch (this.rotate) {
36152             case 0 :
36153                 
36154                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36155                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36156                 
36157                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36158                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36159                 
36160                 var targetWidth = this.minWidth - 2 * x;
36161                 var targetHeight = this.minHeight - 2 * y;
36162                 
36163                 var scale = 1;
36164                 
36165                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36166                     scale = targetWidth / width;
36167                 }
36168                 
36169                 if(x > 0 && y == 0){
36170                     scale = targetHeight / height;
36171                 }
36172                 
36173                 if(x > 0 && y > 0){
36174                     scale = targetWidth / width;
36175                     
36176                     if(width < height){
36177                         scale = targetHeight / height;
36178                     }
36179                 }
36180                 
36181                 context.scale(scale, scale);
36182                 
36183                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36184                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36185
36186                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36187                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36188
36189                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36190                 
36191                 break;
36192             case 90 : 
36193                 
36194                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36195                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36196                 
36197                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36198                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36199                 
36200                 var targetWidth = this.minWidth - 2 * x;
36201                 var targetHeight = this.minHeight - 2 * y;
36202                 
36203                 var scale = 1;
36204                 
36205                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36206                     scale = targetWidth / width;
36207                 }
36208                 
36209                 if(x > 0 && y == 0){
36210                     scale = targetHeight / height;
36211                 }
36212                 
36213                 if(x > 0 && y > 0){
36214                     scale = targetWidth / width;
36215                     
36216                     if(width < height){
36217                         scale = targetHeight / height;
36218                     }
36219                 }
36220                 
36221                 context.scale(scale, scale);
36222                 
36223                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36224                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36225
36226                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36227                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36228                 
36229                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36230                 
36231                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36232                 
36233                 break;
36234             case 180 :
36235                 
36236                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36237                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36238                 
36239                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36240                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36241                 
36242                 var targetWidth = this.minWidth - 2 * x;
36243                 var targetHeight = this.minHeight - 2 * y;
36244                 
36245                 var scale = 1;
36246                 
36247                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36248                     scale = targetWidth / width;
36249                 }
36250                 
36251                 if(x > 0 && y == 0){
36252                     scale = targetHeight / height;
36253                 }
36254                 
36255                 if(x > 0 && y > 0){
36256                     scale = targetWidth / width;
36257                     
36258                     if(width < height){
36259                         scale = targetHeight / height;
36260                     }
36261                 }
36262                 
36263                 context.scale(scale, scale);
36264                 
36265                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36266                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36267
36268                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36269                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36270
36271                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36272                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36273                 
36274                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36275                 
36276                 break;
36277             case 270 :
36278                 
36279                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36280                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36281                 
36282                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36283                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36284                 
36285                 var targetWidth = this.minWidth - 2 * x;
36286                 var targetHeight = this.minHeight - 2 * y;
36287                 
36288                 var scale = 1;
36289                 
36290                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36291                     scale = targetWidth / width;
36292                 }
36293                 
36294                 if(x > 0 && y == 0){
36295                     scale = targetHeight / height;
36296                 }
36297                 
36298                 if(x > 0 && y > 0){
36299                     scale = targetWidth / width;
36300                     
36301                     if(width < height){
36302                         scale = targetHeight / height;
36303                     }
36304                 }
36305                 
36306                 context.scale(scale, scale);
36307                 
36308                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36309                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36310
36311                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36312                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36313                 
36314                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36315                 
36316                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36317                 
36318                 break;
36319             default : 
36320                 break;
36321         }
36322         
36323         this.cropData = canvas.toDataURL(this.cropType);
36324         
36325         if(this.fireEvent('crop', this, this.cropData) !== false){
36326             this.process(this.file, this.cropData);
36327         }
36328         
36329         return;
36330         
36331     },
36332     
36333     setThumbBoxSize : function()
36334     {
36335         var width, height;
36336         
36337         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36338             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36339             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36340             
36341             this.minWidth = width;
36342             this.minHeight = height;
36343             
36344             if(this.rotate == 90 || this.rotate == 270){
36345                 this.minWidth = height;
36346                 this.minHeight = width;
36347             }
36348         }
36349         
36350         height = 300;
36351         width = Math.ceil(this.minWidth * height / this.minHeight);
36352         
36353         if(this.minWidth > this.minHeight){
36354             width = 300;
36355             height = Math.ceil(this.minHeight * width / this.minWidth);
36356         }
36357         
36358         this.thumbEl.setStyle({
36359             width : width + 'px',
36360             height : height + 'px'
36361         });
36362
36363         return;
36364             
36365     },
36366     
36367     setThumbBoxPosition : function()
36368     {
36369         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36370         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36371         
36372         this.thumbEl.setLeft(x);
36373         this.thumbEl.setTop(y);
36374         
36375     },
36376     
36377     baseRotateLevel : function()
36378     {
36379         this.baseRotate = 1;
36380         
36381         if(
36382                 typeof(this.exif) != 'undefined' &&
36383                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36384                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36385         ){
36386             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36387         }
36388         
36389         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36390         
36391     },
36392     
36393     baseScaleLevel : function()
36394     {
36395         var width, height;
36396         
36397         if(this.isDocument){
36398             
36399             if(this.baseRotate == 6 || this.baseRotate == 8){
36400             
36401                 height = this.thumbEl.getHeight();
36402                 this.baseScale = height / this.imageEl.OriginWidth;
36403
36404                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36405                     width = this.thumbEl.getWidth();
36406                     this.baseScale = width / this.imageEl.OriginHeight;
36407                 }
36408
36409                 return;
36410             }
36411
36412             height = this.thumbEl.getHeight();
36413             this.baseScale = height / this.imageEl.OriginHeight;
36414
36415             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36416                 width = this.thumbEl.getWidth();
36417                 this.baseScale = width / this.imageEl.OriginWidth;
36418             }
36419
36420             return;
36421         }
36422         
36423         if(this.baseRotate == 6 || this.baseRotate == 8){
36424             
36425             width = this.thumbEl.getHeight();
36426             this.baseScale = width / this.imageEl.OriginHeight;
36427             
36428             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36429                 height = this.thumbEl.getWidth();
36430                 this.baseScale = height / this.imageEl.OriginHeight;
36431             }
36432             
36433             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36434                 height = this.thumbEl.getWidth();
36435                 this.baseScale = height / this.imageEl.OriginHeight;
36436                 
36437                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36438                     width = this.thumbEl.getHeight();
36439                     this.baseScale = width / this.imageEl.OriginWidth;
36440                 }
36441             }
36442             
36443             return;
36444         }
36445         
36446         width = this.thumbEl.getWidth();
36447         this.baseScale = width / this.imageEl.OriginWidth;
36448         
36449         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36450             height = this.thumbEl.getHeight();
36451             this.baseScale = height / this.imageEl.OriginHeight;
36452         }
36453         
36454         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36455             
36456             height = this.thumbEl.getHeight();
36457             this.baseScale = height / this.imageEl.OriginHeight;
36458             
36459             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36460                 width = this.thumbEl.getWidth();
36461                 this.baseScale = width / this.imageEl.OriginWidth;
36462             }
36463             
36464         }
36465         
36466         return;
36467     },
36468     
36469     getScaleLevel : function()
36470     {
36471         return this.baseScale * Math.pow(1.1, this.scale);
36472     },
36473     
36474     onTouchStart : function(e)
36475     {
36476         if(!this.canvasLoaded){
36477             this.beforeSelectFile(e);
36478             return;
36479         }
36480         
36481         var touches = e.browserEvent.touches;
36482         
36483         if(!touches){
36484             return;
36485         }
36486         
36487         if(touches.length == 1){
36488             this.onMouseDown(e);
36489             return;
36490         }
36491         
36492         if(touches.length != 2){
36493             return;
36494         }
36495         
36496         var coords = [];
36497         
36498         for(var i = 0, finger; finger = touches[i]; i++){
36499             coords.push(finger.pageX, finger.pageY);
36500         }
36501         
36502         var x = Math.pow(coords[0] - coords[2], 2);
36503         var y = Math.pow(coords[1] - coords[3], 2);
36504         
36505         this.startDistance = Math.sqrt(x + y);
36506         
36507         this.startScale = this.scale;
36508         
36509         this.pinching = true;
36510         this.dragable = false;
36511         
36512     },
36513     
36514     onTouchMove : function(e)
36515     {
36516         if(!this.pinching && !this.dragable){
36517             return;
36518         }
36519         
36520         var touches = e.browserEvent.touches;
36521         
36522         if(!touches){
36523             return;
36524         }
36525         
36526         if(this.dragable){
36527             this.onMouseMove(e);
36528             return;
36529         }
36530         
36531         var coords = [];
36532         
36533         for(var i = 0, finger; finger = touches[i]; i++){
36534             coords.push(finger.pageX, finger.pageY);
36535         }
36536         
36537         var x = Math.pow(coords[0] - coords[2], 2);
36538         var y = Math.pow(coords[1] - coords[3], 2);
36539         
36540         this.endDistance = Math.sqrt(x + y);
36541         
36542         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36543         
36544         if(!this.zoomable()){
36545             this.scale = this.startScale;
36546             return;
36547         }
36548         
36549         this.draw();
36550         
36551     },
36552     
36553     onTouchEnd : function(e)
36554     {
36555         this.pinching = false;
36556         this.dragable = false;
36557         
36558     },
36559     
36560     process : function(file, crop)
36561     {
36562         if(this.loadMask){
36563             this.maskEl.mask(this.loadingText);
36564         }
36565         
36566         this.xhr = new XMLHttpRequest();
36567         
36568         file.xhr = this.xhr;
36569
36570         this.xhr.open(this.method, this.url, true);
36571         
36572         var headers = {
36573             "Accept": "application/json",
36574             "Cache-Control": "no-cache",
36575             "X-Requested-With": "XMLHttpRequest"
36576         };
36577         
36578         for (var headerName in headers) {
36579             var headerValue = headers[headerName];
36580             if (headerValue) {
36581                 this.xhr.setRequestHeader(headerName, headerValue);
36582             }
36583         }
36584         
36585         var _this = this;
36586         
36587         this.xhr.onload = function()
36588         {
36589             _this.xhrOnLoad(_this.xhr);
36590         }
36591         
36592         this.xhr.onerror = function()
36593         {
36594             _this.xhrOnError(_this.xhr);
36595         }
36596         
36597         var formData = new FormData();
36598
36599         formData.append('returnHTML', 'NO');
36600         
36601         if(crop){
36602             formData.append('crop', crop);
36603         }
36604         
36605         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36606             formData.append(this.paramName, file, file.name);
36607         }
36608         
36609         if(typeof(file.filename) != 'undefined'){
36610             formData.append('filename', file.filename);
36611         }
36612         
36613         if(typeof(file.mimetype) != 'undefined'){
36614             formData.append('mimetype', file.mimetype);
36615         }
36616         
36617         if(this.fireEvent('arrange', this, formData) != false){
36618             this.xhr.send(formData);
36619         };
36620     },
36621     
36622     xhrOnLoad : function(xhr)
36623     {
36624         if(this.loadMask){
36625             this.maskEl.unmask();
36626         }
36627         
36628         if (xhr.readyState !== 4) {
36629             this.fireEvent('exception', this, xhr);
36630             return;
36631         }
36632
36633         var response = Roo.decode(xhr.responseText);
36634         
36635         if(!response.success){
36636             this.fireEvent('exception', this, xhr);
36637             return;
36638         }
36639         
36640         var response = Roo.decode(xhr.responseText);
36641         
36642         this.fireEvent('upload', this, response);
36643         
36644     },
36645     
36646     xhrOnError : function()
36647     {
36648         if(this.loadMask){
36649             this.maskEl.unmask();
36650         }
36651         
36652         Roo.log('xhr on error');
36653         
36654         var response = Roo.decode(xhr.responseText);
36655           
36656         Roo.log(response);
36657         
36658     },
36659     
36660     prepare : function(file)
36661     {   
36662         if(this.loadMask){
36663             this.maskEl.mask(this.loadingText);
36664         }
36665         
36666         this.file = false;
36667         this.exif = {};
36668         
36669         if(typeof(file) === 'string'){
36670             this.loadCanvas(file);
36671             return;
36672         }
36673         
36674         if(!file || !this.urlAPI){
36675             return;
36676         }
36677         
36678         this.file = file;
36679         this.cropType = file.type;
36680         
36681         var _this = this;
36682         
36683         if(this.fireEvent('prepare', this, this.file) != false){
36684             
36685             var reader = new FileReader();
36686             
36687             reader.onload = function (e) {
36688                 if (e.target.error) {
36689                     Roo.log(e.target.error);
36690                     return;
36691                 }
36692                 
36693                 var buffer = e.target.result,
36694                     dataView = new DataView(buffer),
36695                     offset = 2,
36696                     maxOffset = dataView.byteLength - 4,
36697                     markerBytes,
36698                     markerLength;
36699                 
36700                 if (dataView.getUint16(0) === 0xffd8) {
36701                     while (offset < maxOffset) {
36702                         markerBytes = dataView.getUint16(offset);
36703                         
36704                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36705                             markerLength = dataView.getUint16(offset + 2) + 2;
36706                             if (offset + markerLength > dataView.byteLength) {
36707                                 Roo.log('Invalid meta data: Invalid segment size.');
36708                                 break;
36709                             }
36710                             
36711                             if(markerBytes == 0xffe1){
36712                                 _this.parseExifData(
36713                                     dataView,
36714                                     offset,
36715                                     markerLength
36716                                 );
36717                             }
36718                             
36719                             offset += markerLength;
36720                             
36721                             continue;
36722                         }
36723                         
36724                         break;
36725                     }
36726                     
36727                 }
36728                 
36729                 var url = _this.urlAPI.createObjectURL(_this.file);
36730                 
36731                 _this.loadCanvas(url);
36732                 
36733                 return;
36734             }
36735             
36736             reader.readAsArrayBuffer(this.file);
36737             
36738         }
36739         
36740     },
36741     
36742     parseExifData : function(dataView, offset, length)
36743     {
36744         var tiffOffset = offset + 10,
36745             littleEndian,
36746             dirOffset;
36747     
36748         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36749             // No Exif data, might be XMP data instead
36750             return;
36751         }
36752         
36753         // Check for the ASCII code for "Exif" (0x45786966):
36754         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36755             // No Exif data, might be XMP data instead
36756             return;
36757         }
36758         if (tiffOffset + 8 > dataView.byteLength) {
36759             Roo.log('Invalid Exif data: Invalid segment size.');
36760             return;
36761         }
36762         // Check for the two null bytes:
36763         if (dataView.getUint16(offset + 8) !== 0x0000) {
36764             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36765             return;
36766         }
36767         // Check the byte alignment:
36768         switch (dataView.getUint16(tiffOffset)) {
36769         case 0x4949:
36770             littleEndian = true;
36771             break;
36772         case 0x4D4D:
36773             littleEndian = false;
36774             break;
36775         default:
36776             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36777             return;
36778         }
36779         // Check for the TIFF tag marker (0x002A):
36780         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36781             Roo.log('Invalid Exif data: Missing TIFF marker.');
36782             return;
36783         }
36784         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36785         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36786         
36787         this.parseExifTags(
36788             dataView,
36789             tiffOffset,
36790             tiffOffset + dirOffset,
36791             littleEndian
36792         );
36793     },
36794     
36795     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36796     {
36797         var tagsNumber,
36798             dirEndOffset,
36799             i;
36800         if (dirOffset + 6 > dataView.byteLength) {
36801             Roo.log('Invalid Exif data: Invalid directory offset.');
36802             return;
36803         }
36804         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36805         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36806         if (dirEndOffset + 4 > dataView.byteLength) {
36807             Roo.log('Invalid Exif data: Invalid directory size.');
36808             return;
36809         }
36810         for (i = 0; i < tagsNumber; i += 1) {
36811             this.parseExifTag(
36812                 dataView,
36813                 tiffOffset,
36814                 dirOffset + 2 + 12 * i, // tag offset
36815                 littleEndian
36816             );
36817         }
36818         // Return the offset to the next directory:
36819         return dataView.getUint32(dirEndOffset, littleEndian);
36820     },
36821     
36822     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36823     {
36824         var tag = dataView.getUint16(offset, littleEndian);
36825         
36826         this.exif[tag] = this.getExifValue(
36827             dataView,
36828             tiffOffset,
36829             offset,
36830             dataView.getUint16(offset + 2, littleEndian), // tag type
36831             dataView.getUint32(offset + 4, littleEndian), // tag length
36832             littleEndian
36833         );
36834     },
36835     
36836     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36837     {
36838         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36839             tagSize,
36840             dataOffset,
36841             values,
36842             i,
36843             str,
36844             c;
36845     
36846         if (!tagType) {
36847             Roo.log('Invalid Exif data: Invalid tag type.');
36848             return;
36849         }
36850         
36851         tagSize = tagType.size * length;
36852         // Determine if the value is contained in the dataOffset bytes,
36853         // or if the value at the dataOffset is a pointer to the actual data:
36854         dataOffset = tagSize > 4 ?
36855                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36856         if (dataOffset + tagSize > dataView.byteLength) {
36857             Roo.log('Invalid Exif data: Invalid data offset.');
36858             return;
36859         }
36860         if (length === 1) {
36861             return tagType.getValue(dataView, dataOffset, littleEndian);
36862         }
36863         values = [];
36864         for (i = 0; i < length; i += 1) {
36865             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36866         }
36867         
36868         if (tagType.ascii) {
36869             str = '';
36870             // Concatenate the chars:
36871             for (i = 0; i < values.length; i += 1) {
36872                 c = values[i];
36873                 // Ignore the terminating NULL byte(s):
36874                 if (c === '\u0000') {
36875                     break;
36876                 }
36877                 str += c;
36878             }
36879             return str;
36880         }
36881         return values;
36882     }
36883     
36884 });
36885
36886 Roo.apply(Roo.bootstrap.UploadCropbox, {
36887     tags : {
36888         'Orientation': 0x0112
36889     },
36890     
36891     Orientation: {
36892             1: 0, //'top-left',
36893 //            2: 'top-right',
36894             3: 180, //'bottom-right',
36895 //            4: 'bottom-left',
36896 //            5: 'left-top',
36897             6: 90, //'right-top',
36898 //            7: 'right-bottom',
36899             8: 270 //'left-bottom'
36900     },
36901     
36902     exifTagTypes : {
36903         // byte, 8-bit unsigned int:
36904         1: {
36905             getValue: function (dataView, dataOffset) {
36906                 return dataView.getUint8(dataOffset);
36907             },
36908             size: 1
36909         },
36910         // ascii, 8-bit byte:
36911         2: {
36912             getValue: function (dataView, dataOffset) {
36913                 return String.fromCharCode(dataView.getUint8(dataOffset));
36914             },
36915             size: 1,
36916             ascii: true
36917         },
36918         // short, 16 bit int:
36919         3: {
36920             getValue: function (dataView, dataOffset, littleEndian) {
36921                 return dataView.getUint16(dataOffset, littleEndian);
36922             },
36923             size: 2
36924         },
36925         // long, 32 bit int:
36926         4: {
36927             getValue: function (dataView, dataOffset, littleEndian) {
36928                 return dataView.getUint32(dataOffset, littleEndian);
36929             },
36930             size: 4
36931         },
36932         // rational = two long values, first is numerator, second is denominator:
36933         5: {
36934             getValue: function (dataView, dataOffset, littleEndian) {
36935                 return dataView.getUint32(dataOffset, littleEndian) /
36936                     dataView.getUint32(dataOffset + 4, littleEndian);
36937             },
36938             size: 8
36939         },
36940         // slong, 32 bit signed int:
36941         9: {
36942             getValue: function (dataView, dataOffset, littleEndian) {
36943                 return dataView.getInt32(dataOffset, littleEndian);
36944             },
36945             size: 4
36946         },
36947         // srational, two slongs, first is numerator, second is denominator:
36948         10: {
36949             getValue: function (dataView, dataOffset, littleEndian) {
36950                 return dataView.getInt32(dataOffset, littleEndian) /
36951                     dataView.getInt32(dataOffset + 4, littleEndian);
36952             },
36953             size: 8
36954         }
36955     },
36956     
36957     footer : {
36958         STANDARD : [
36959             {
36960                 tag : 'div',
36961                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36962                 action : 'rotate-left',
36963                 cn : [
36964                     {
36965                         tag : 'button',
36966                         cls : 'btn btn-default',
36967                         html : '<i class="fa fa-undo"></i>'
36968                     }
36969                 ]
36970             },
36971             {
36972                 tag : 'div',
36973                 cls : 'btn-group roo-upload-cropbox-picture',
36974                 action : 'picture',
36975                 cn : [
36976                     {
36977                         tag : 'button',
36978                         cls : 'btn btn-default',
36979                         html : '<i class="fa fa-picture-o"></i>'
36980                     }
36981                 ]
36982             },
36983             {
36984                 tag : 'div',
36985                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36986                 action : 'rotate-right',
36987                 cn : [
36988                     {
36989                         tag : 'button',
36990                         cls : 'btn btn-default',
36991                         html : '<i class="fa fa-repeat"></i>'
36992                     }
36993                 ]
36994             }
36995         ],
36996         DOCUMENT : [
36997             {
36998                 tag : 'div',
36999                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37000                 action : 'rotate-left',
37001                 cn : [
37002                     {
37003                         tag : 'button',
37004                         cls : 'btn btn-default',
37005                         html : '<i class="fa fa-undo"></i>'
37006                     }
37007                 ]
37008             },
37009             {
37010                 tag : 'div',
37011                 cls : 'btn-group roo-upload-cropbox-download',
37012                 action : 'download',
37013                 cn : [
37014                     {
37015                         tag : 'button',
37016                         cls : 'btn btn-default',
37017                         html : '<i class="fa fa-download"></i>'
37018                     }
37019                 ]
37020             },
37021             {
37022                 tag : 'div',
37023                 cls : 'btn-group roo-upload-cropbox-crop',
37024                 action : 'crop',
37025                 cn : [
37026                     {
37027                         tag : 'button',
37028                         cls : 'btn btn-default',
37029                         html : '<i class="fa fa-crop"></i>'
37030                     }
37031                 ]
37032             },
37033             {
37034                 tag : 'div',
37035                 cls : 'btn-group roo-upload-cropbox-trash',
37036                 action : 'trash',
37037                 cn : [
37038                     {
37039                         tag : 'button',
37040                         cls : 'btn btn-default',
37041                         html : '<i class="fa fa-trash"></i>'
37042                     }
37043                 ]
37044             },
37045             {
37046                 tag : 'div',
37047                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37048                 action : 'rotate-right',
37049                 cn : [
37050                     {
37051                         tag : 'button',
37052                         cls : 'btn btn-default',
37053                         html : '<i class="fa fa-repeat"></i>'
37054                     }
37055                 ]
37056             }
37057         ],
37058         ROTATOR : [
37059             {
37060                 tag : 'div',
37061                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37062                 action : 'rotate-left',
37063                 cn : [
37064                     {
37065                         tag : 'button',
37066                         cls : 'btn btn-default',
37067                         html : '<i class="fa fa-undo"></i>'
37068                     }
37069                 ]
37070             },
37071             {
37072                 tag : 'div',
37073                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37074                 action : 'rotate-right',
37075                 cn : [
37076                     {
37077                         tag : 'button',
37078                         cls : 'btn btn-default',
37079                         html : '<i class="fa fa-repeat"></i>'
37080                     }
37081                 ]
37082             }
37083         ]
37084     }
37085 });
37086
37087 /*
37088 * Licence: LGPL
37089 */
37090
37091 /**
37092  * @class Roo.bootstrap.DocumentManager
37093  * @extends Roo.bootstrap.Component
37094  * Bootstrap DocumentManager class
37095  * @cfg {String} paramName default 'imageUpload'
37096  * @cfg {String} toolTipName default 'filename'
37097  * @cfg {String} method default POST
37098  * @cfg {String} url action url
37099  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37100  * @cfg {Boolean} multiple multiple upload default true
37101  * @cfg {Number} thumbSize default 300
37102  * @cfg {String} fieldLabel
37103  * @cfg {Number} labelWidth default 4
37104  * @cfg {String} labelAlign (left|top) default left
37105  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37106 * @cfg {Number} labellg set the width of label (1-12)
37107  * @cfg {Number} labelmd set the width of label (1-12)
37108  * @cfg {Number} labelsm set the width of label (1-12)
37109  * @cfg {Number} labelxs set the width of label (1-12)
37110  * 
37111  * @constructor
37112  * Create a new DocumentManager
37113  * @param {Object} config The config object
37114  */
37115
37116 Roo.bootstrap.DocumentManager = function(config){
37117     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37118     
37119     this.files = [];
37120     this.delegates = [];
37121     
37122     this.addEvents({
37123         /**
37124          * @event initial
37125          * Fire when initial the DocumentManager
37126          * @param {Roo.bootstrap.DocumentManager} this
37127          */
37128         "initial" : true,
37129         /**
37130          * @event inspect
37131          * inspect selected file
37132          * @param {Roo.bootstrap.DocumentManager} this
37133          * @param {File} file
37134          */
37135         "inspect" : true,
37136         /**
37137          * @event exception
37138          * Fire when xhr load exception
37139          * @param {Roo.bootstrap.DocumentManager} this
37140          * @param {XMLHttpRequest} xhr
37141          */
37142         "exception" : true,
37143         /**
37144          * @event afterupload
37145          * Fire when xhr load exception
37146          * @param {Roo.bootstrap.DocumentManager} this
37147          * @param {XMLHttpRequest} xhr
37148          */
37149         "afterupload" : true,
37150         /**
37151          * @event prepare
37152          * prepare the form data
37153          * @param {Roo.bootstrap.DocumentManager} this
37154          * @param {Object} formData
37155          */
37156         "prepare" : true,
37157         /**
37158          * @event remove
37159          * Fire when remove the file
37160          * @param {Roo.bootstrap.DocumentManager} this
37161          * @param {Object} file
37162          */
37163         "remove" : true,
37164         /**
37165          * @event refresh
37166          * Fire after refresh the file
37167          * @param {Roo.bootstrap.DocumentManager} this
37168          */
37169         "refresh" : true,
37170         /**
37171          * @event click
37172          * Fire after click the image
37173          * @param {Roo.bootstrap.DocumentManager} this
37174          * @param {Object} file
37175          */
37176         "click" : true,
37177         /**
37178          * @event edit
37179          * Fire when upload a image and editable set to true
37180          * @param {Roo.bootstrap.DocumentManager} this
37181          * @param {Object} file
37182          */
37183         "edit" : true,
37184         /**
37185          * @event beforeselectfile
37186          * Fire before select file
37187          * @param {Roo.bootstrap.DocumentManager} this
37188          */
37189         "beforeselectfile" : true,
37190         /**
37191          * @event process
37192          * Fire before process file
37193          * @param {Roo.bootstrap.DocumentManager} this
37194          * @param {Object} file
37195          */
37196         "process" : true,
37197         /**
37198          * @event previewrendered
37199          * Fire when preview rendered
37200          * @param {Roo.bootstrap.DocumentManager} this
37201          * @param {Object} file
37202          */
37203         "previewrendered" : true,
37204         /**
37205          */
37206         "previewResize" : true
37207         
37208     });
37209 };
37210
37211 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37212     
37213     boxes : 0,
37214     inputName : '',
37215     thumbSize : 300,
37216     multiple : true,
37217     files : false,
37218     method : 'POST',
37219     url : '',
37220     paramName : 'imageUpload',
37221     toolTipName : 'filename',
37222     fieldLabel : '',
37223     labelWidth : 4,
37224     labelAlign : 'left',
37225     editable : true,
37226     delegates : false,
37227     xhr : false, 
37228     
37229     labellg : 0,
37230     labelmd : 0,
37231     labelsm : 0,
37232     labelxs : 0,
37233     
37234     getAutoCreate : function()
37235     {   
37236         var managerWidget = {
37237             tag : 'div',
37238             cls : 'roo-document-manager',
37239             cn : [
37240                 {
37241                     tag : 'input',
37242                     cls : 'roo-document-manager-selector',
37243                     type : 'file'
37244                 },
37245                 {
37246                     tag : 'div',
37247                     cls : 'roo-document-manager-uploader',
37248                     cn : [
37249                         {
37250                             tag : 'div',
37251                             cls : 'roo-document-manager-upload-btn',
37252                             html : '<i class="fa fa-plus"></i>'
37253                         }
37254                     ]
37255                     
37256                 }
37257             ]
37258         };
37259         
37260         var content = [
37261             {
37262                 tag : 'div',
37263                 cls : 'column col-md-12',
37264                 cn : managerWidget
37265             }
37266         ];
37267         
37268         if(this.fieldLabel.length){
37269             
37270             content = [
37271                 {
37272                     tag : 'div',
37273                     cls : 'column col-md-12',
37274                     html : this.fieldLabel
37275                 },
37276                 {
37277                     tag : 'div',
37278                     cls : 'column col-md-12',
37279                     cn : managerWidget
37280                 }
37281             ];
37282
37283             if(this.labelAlign == 'left'){
37284                 content = [
37285                     {
37286                         tag : 'div',
37287                         cls : 'column',
37288                         html : this.fieldLabel
37289                     },
37290                     {
37291                         tag : 'div',
37292                         cls : 'column',
37293                         cn : managerWidget
37294                     }
37295                 ];
37296                 
37297                 if(this.labelWidth > 12){
37298                     content[0].style = "width: " + this.labelWidth + 'px';
37299                 }
37300
37301                 if(this.labelWidth < 13 && this.labelmd == 0){
37302                     this.labelmd = this.labelWidth;
37303                 }
37304
37305                 if(this.labellg > 0){
37306                     content[0].cls += ' col-lg-' + this.labellg;
37307                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37308                 }
37309
37310                 if(this.labelmd > 0){
37311                     content[0].cls += ' col-md-' + this.labelmd;
37312                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37313                 }
37314
37315                 if(this.labelsm > 0){
37316                     content[0].cls += ' col-sm-' + this.labelsm;
37317                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37318                 }
37319
37320                 if(this.labelxs > 0){
37321                     content[0].cls += ' col-xs-' + this.labelxs;
37322                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37323                 }
37324                 
37325             }
37326         }
37327         
37328         var cfg = {
37329             tag : 'div',
37330             cls : 'row clearfix',
37331             cn : content
37332         };
37333         
37334         return cfg;
37335         
37336     },
37337     
37338     initEvents : function()
37339     {
37340         this.managerEl = this.el.select('.roo-document-manager', true).first();
37341         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37342         
37343         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37344         this.selectorEl.hide();
37345         
37346         if(this.multiple){
37347             this.selectorEl.attr('multiple', 'multiple');
37348         }
37349         
37350         this.selectorEl.on('change', this.onFileSelected, this);
37351         
37352         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37353         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37354         
37355         this.uploader.on('click', this.onUploaderClick, this);
37356         
37357         this.renderProgressDialog();
37358         
37359         var _this = this;
37360         
37361         window.addEventListener("resize", function() { _this.refresh(); } );
37362         
37363         this.fireEvent('initial', this);
37364     },
37365     
37366     renderProgressDialog : function()
37367     {
37368         var _this = this;
37369         
37370         this.progressDialog = new Roo.bootstrap.Modal({
37371             cls : 'roo-document-manager-progress-dialog',
37372             allow_close : false,
37373             animate : false,
37374             title : '',
37375             buttons : [
37376                 {
37377                     name  :'cancel',
37378                     weight : 'danger',
37379                     html : 'Cancel'
37380                 }
37381             ], 
37382             listeners : { 
37383                 btnclick : function() {
37384                     _this.uploadCancel();
37385                     this.hide();
37386                 }
37387             }
37388         });
37389          
37390         this.progressDialog.render(Roo.get(document.body));
37391          
37392         this.progress = new Roo.bootstrap.Progress({
37393             cls : 'roo-document-manager-progress',
37394             active : true,
37395             striped : true
37396         });
37397         
37398         this.progress.render(this.progressDialog.getChildContainer());
37399         
37400         this.progressBar = new Roo.bootstrap.ProgressBar({
37401             cls : 'roo-document-manager-progress-bar',
37402             aria_valuenow : 0,
37403             aria_valuemin : 0,
37404             aria_valuemax : 12,
37405             panel : 'success'
37406         });
37407         
37408         this.progressBar.render(this.progress.getChildContainer());
37409     },
37410     
37411     onUploaderClick : function(e)
37412     {
37413         e.preventDefault();
37414      
37415         if(this.fireEvent('beforeselectfile', this) != false){
37416             this.selectorEl.dom.click();
37417         }
37418         
37419     },
37420     
37421     onFileSelected : function(e)
37422     {
37423         e.preventDefault();
37424         
37425         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37426             return;
37427         }
37428         
37429         Roo.each(this.selectorEl.dom.files, function(file){
37430             if(this.fireEvent('inspect', this, file) != false){
37431                 this.files.push(file);
37432             }
37433         }, this);
37434         
37435         this.queue();
37436         
37437     },
37438     
37439     queue : function()
37440     {
37441         this.selectorEl.dom.value = '';
37442         
37443         if(!this.files || !this.files.length){
37444             return;
37445         }
37446         
37447         if(this.boxes > 0 && this.files.length > this.boxes){
37448             this.files = this.files.slice(0, this.boxes);
37449         }
37450         
37451         this.uploader.show();
37452         
37453         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37454             this.uploader.hide();
37455         }
37456         
37457         var _this = this;
37458         
37459         var files = [];
37460         
37461         var docs = [];
37462         
37463         Roo.each(this.files, function(file){
37464             
37465             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37466                 var f = this.renderPreview(file);
37467                 files.push(f);
37468                 return;
37469             }
37470             
37471             if(file.type.indexOf('image') != -1){
37472                 this.delegates.push(
37473                     (function(){
37474                         _this.process(file);
37475                     }).createDelegate(this)
37476                 );
37477         
37478                 return;
37479             }
37480             
37481             docs.push(
37482                 (function(){
37483                     _this.process(file);
37484                 }).createDelegate(this)
37485             );
37486             
37487         }, this);
37488         
37489         this.files = files;
37490         
37491         this.delegates = this.delegates.concat(docs);
37492         
37493         if(!this.delegates.length){
37494             this.refresh();
37495             return;
37496         }
37497         
37498         this.progressBar.aria_valuemax = this.delegates.length;
37499         
37500         this.arrange();
37501         
37502         return;
37503     },
37504     
37505     arrange : function()
37506     {
37507         if(!this.delegates.length){
37508             this.progressDialog.hide();
37509             this.refresh();
37510             return;
37511         }
37512         
37513         var delegate = this.delegates.shift();
37514         
37515         this.progressDialog.show();
37516         
37517         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37518         
37519         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37520         
37521         delegate();
37522     },
37523     
37524     refresh : function()
37525     {
37526         this.uploader.show();
37527         
37528         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37529             this.uploader.hide();
37530         }
37531         
37532         Roo.isTouch ? this.closable(false) : this.closable(true);
37533         
37534         this.fireEvent('refresh', this);
37535     },
37536     
37537     onRemove : function(e, el, o)
37538     {
37539         e.preventDefault();
37540         
37541         this.fireEvent('remove', this, o);
37542         
37543     },
37544     
37545     remove : function(o)
37546     {
37547         var files = [];
37548         
37549         Roo.each(this.files, function(file){
37550             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37551                 files.push(file);
37552                 return;
37553             }
37554
37555             o.target.remove();
37556
37557         }, this);
37558         
37559         this.files = files;
37560         
37561         this.refresh();
37562     },
37563     
37564     clear : function()
37565     {
37566         Roo.each(this.files, function(file){
37567             if(!file.target){
37568                 return;
37569             }
37570             
37571             file.target.remove();
37572
37573         }, this);
37574         
37575         this.files = [];
37576         
37577         this.refresh();
37578     },
37579     
37580     onClick : function(e, el, o)
37581     {
37582         e.preventDefault();
37583         
37584         this.fireEvent('click', this, o);
37585         
37586     },
37587     
37588     closable : function(closable)
37589     {
37590         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37591             
37592             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37593             
37594             if(closable){
37595                 el.show();
37596                 return;
37597             }
37598             
37599             el.hide();
37600             
37601         }, this);
37602     },
37603     
37604     xhrOnLoad : function(xhr)
37605     {
37606         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37607             el.remove();
37608         }, this);
37609         
37610         if (xhr.readyState !== 4) {
37611             this.arrange();
37612             this.fireEvent('exception', this, xhr);
37613             return;
37614         }
37615
37616         var response = Roo.decode(xhr.responseText);
37617         
37618         if(!response.success){
37619             this.arrange();
37620             this.fireEvent('exception', this, xhr);
37621             return;
37622         }
37623         
37624         var file = this.renderPreview(response.data);
37625         
37626         this.files.push(file);
37627         
37628         this.arrange();
37629         
37630         this.fireEvent('afterupload', this, xhr);
37631         
37632     },
37633     
37634     xhrOnError : function(xhr)
37635     {
37636         Roo.log('xhr on error');
37637         
37638         var response = Roo.decode(xhr.responseText);
37639           
37640         Roo.log(response);
37641         
37642         this.arrange();
37643     },
37644     
37645     process : function(file)
37646     {
37647         if(this.fireEvent('process', this, file) !== false){
37648             if(this.editable && file.type.indexOf('image') != -1){
37649                 this.fireEvent('edit', this, file);
37650                 return;
37651             }
37652
37653             this.uploadStart(file, false);
37654
37655             return;
37656         }
37657         
37658     },
37659     
37660     uploadStart : function(file, crop)
37661     {
37662         this.xhr = new XMLHttpRequest();
37663         
37664         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37665             this.arrange();
37666             return;
37667         }
37668         
37669         file.xhr = this.xhr;
37670             
37671         this.managerEl.createChild({
37672             tag : 'div',
37673             cls : 'roo-document-manager-loading',
37674             cn : [
37675                 {
37676                     tag : 'div',
37677                     tooltip : file.name,
37678                     cls : 'roo-document-manager-thumb',
37679                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37680                 }
37681             ]
37682
37683         });
37684
37685         this.xhr.open(this.method, this.url, true);
37686         
37687         var headers = {
37688             "Accept": "application/json",
37689             "Cache-Control": "no-cache",
37690             "X-Requested-With": "XMLHttpRequest"
37691         };
37692         
37693         for (var headerName in headers) {
37694             var headerValue = headers[headerName];
37695             if (headerValue) {
37696                 this.xhr.setRequestHeader(headerName, headerValue);
37697             }
37698         }
37699         
37700         var _this = this;
37701         
37702         this.xhr.onload = function()
37703         {
37704             _this.xhrOnLoad(_this.xhr);
37705         }
37706         
37707         this.xhr.onerror = function()
37708         {
37709             _this.xhrOnError(_this.xhr);
37710         }
37711         
37712         var formData = new FormData();
37713
37714         formData.append('returnHTML', 'NO');
37715         
37716         if(crop){
37717             formData.append('crop', crop);
37718         }
37719         
37720         formData.append(this.paramName, file, file.name);
37721         
37722         var options = {
37723             file : file, 
37724             manually : false
37725         };
37726         
37727         if(this.fireEvent('prepare', this, formData, options) != false){
37728             
37729             if(options.manually){
37730                 return;
37731             }
37732             
37733             this.xhr.send(formData);
37734             return;
37735         };
37736         
37737         this.uploadCancel();
37738     },
37739     
37740     uploadCancel : function()
37741     {
37742         if (this.xhr) {
37743             this.xhr.abort();
37744         }
37745         
37746         this.delegates = [];
37747         
37748         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37749             el.remove();
37750         }, this);
37751         
37752         this.arrange();
37753     },
37754     
37755     renderPreview : function(file)
37756     {
37757         if(typeof(file.target) != 'undefined' && file.target){
37758             return file;
37759         }
37760         
37761         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37762         
37763         var previewEl = this.managerEl.createChild({
37764             tag : 'div',
37765             cls : 'roo-document-manager-preview',
37766             cn : [
37767                 {
37768                     tag : 'div',
37769                     tooltip : file[this.toolTipName],
37770                     cls : 'roo-document-manager-thumb',
37771                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37772                 },
37773                 {
37774                     tag : 'button',
37775                     cls : 'close',
37776                     html : '<i class="fa fa-times-circle"></i>'
37777                 }
37778             ]
37779         });
37780
37781         var close = previewEl.select('button.close', true).first();
37782
37783         close.on('click', this.onRemove, this, file);
37784
37785         file.target = previewEl;
37786
37787         var image = previewEl.select('img', true).first();
37788         
37789         var _this = this;
37790         
37791         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37792         
37793         image.on('click', this.onClick, this, file);
37794         
37795         this.fireEvent('previewrendered', this, file);
37796         
37797         return file;
37798         
37799     },
37800     
37801     onPreviewLoad : function(file, image)
37802     {
37803         if(typeof(file.target) == 'undefined' || !file.target){
37804             return;
37805         }
37806         
37807         var width = image.dom.naturalWidth || image.dom.width;
37808         var height = image.dom.naturalHeight || image.dom.height;
37809         
37810         if(!this.previewResize) {
37811             return;
37812         }
37813         
37814         if(width > height){
37815             file.target.addClass('wide');
37816             return;
37817         }
37818         
37819         file.target.addClass('tall');
37820         return;
37821         
37822     },
37823     
37824     uploadFromSource : function(file, crop)
37825     {
37826         this.xhr = new XMLHttpRequest();
37827         
37828         this.managerEl.createChild({
37829             tag : 'div',
37830             cls : 'roo-document-manager-loading',
37831             cn : [
37832                 {
37833                     tag : 'div',
37834                     tooltip : file.name,
37835                     cls : 'roo-document-manager-thumb',
37836                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37837                 }
37838             ]
37839
37840         });
37841
37842         this.xhr.open(this.method, this.url, true);
37843         
37844         var headers = {
37845             "Accept": "application/json",
37846             "Cache-Control": "no-cache",
37847             "X-Requested-With": "XMLHttpRequest"
37848         };
37849         
37850         for (var headerName in headers) {
37851             var headerValue = headers[headerName];
37852             if (headerValue) {
37853                 this.xhr.setRequestHeader(headerName, headerValue);
37854             }
37855         }
37856         
37857         var _this = this;
37858         
37859         this.xhr.onload = function()
37860         {
37861             _this.xhrOnLoad(_this.xhr);
37862         }
37863         
37864         this.xhr.onerror = function()
37865         {
37866             _this.xhrOnError(_this.xhr);
37867         }
37868         
37869         var formData = new FormData();
37870
37871         formData.append('returnHTML', 'NO');
37872         
37873         formData.append('crop', crop);
37874         
37875         if(typeof(file.filename) != 'undefined'){
37876             formData.append('filename', file.filename);
37877         }
37878         
37879         if(typeof(file.mimetype) != 'undefined'){
37880             formData.append('mimetype', file.mimetype);
37881         }
37882         
37883         Roo.log(formData);
37884         
37885         if(this.fireEvent('prepare', this, formData) != false){
37886             this.xhr.send(formData);
37887         };
37888     }
37889 });
37890
37891 /*
37892 * Licence: LGPL
37893 */
37894
37895 /**
37896  * @class Roo.bootstrap.DocumentViewer
37897  * @extends Roo.bootstrap.Component
37898  * Bootstrap DocumentViewer class
37899  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37900  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37901  * 
37902  * @constructor
37903  * Create a new DocumentViewer
37904  * @param {Object} config The config object
37905  */
37906
37907 Roo.bootstrap.DocumentViewer = function(config){
37908     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37909     
37910     this.addEvents({
37911         /**
37912          * @event initial
37913          * Fire after initEvent
37914          * @param {Roo.bootstrap.DocumentViewer} this
37915          */
37916         "initial" : true,
37917         /**
37918          * @event click
37919          * Fire after click
37920          * @param {Roo.bootstrap.DocumentViewer} this
37921          */
37922         "click" : true,
37923         /**
37924          * @event download
37925          * Fire after download button
37926          * @param {Roo.bootstrap.DocumentViewer} this
37927          */
37928         "download" : true,
37929         /**
37930          * @event trash
37931          * Fire after trash button
37932          * @param {Roo.bootstrap.DocumentViewer} this
37933          */
37934         "trash" : true
37935         
37936     });
37937 };
37938
37939 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37940     
37941     showDownload : true,
37942     
37943     showTrash : true,
37944     
37945     getAutoCreate : function()
37946     {
37947         var cfg = {
37948             tag : 'div',
37949             cls : 'roo-document-viewer',
37950             cn : [
37951                 {
37952                     tag : 'div',
37953                     cls : 'roo-document-viewer-body',
37954                     cn : [
37955                         {
37956                             tag : 'div',
37957                             cls : 'roo-document-viewer-thumb',
37958                             cn : [
37959                                 {
37960                                     tag : 'img',
37961                                     cls : 'roo-document-viewer-image'
37962                                 }
37963                             ]
37964                         }
37965                     ]
37966                 },
37967                 {
37968                     tag : 'div',
37969                     cls : 'roo-document-viewer-footer',
37970                     cn : {
37971                         tag : 'div',
37972                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37973                         cn : [
37974                             {
37975                                 tag : 'div',
37976                                 cls : 'btn-group roo-document-viewer-download',
37977                                 cn : [
37978                                     {
37979                                         tag : 'button',
37980                                         cls : 'btn btn-default',
37981                                         html : '<i class="fa fa-download"></i>'
37982                                     }
37983                                 ]
37984                             },
37985                             {
37986                                 tag : 'div',
37987                                 cls : 'btn-group roo-document-viewer-trash',
37988                                 cn : [
37989                                     {
37990                                         tag : 'button',
37991                                         cls : 'btn btn-default',
37992                                         html : '<i class="fa fa-trash"></i>'
37993                                     }
37994                                 ]
37995                             }
37996                         ]
37997                     }
37998                 }
37999             ]
38000         };
38001         
38002         return cfg;
38003     },
38004     
38005     initEvents : function()
38006     {
38007         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38008         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38009         
38010         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38011         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38012         
38013         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38014         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38015         
38016         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38017         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38018         
38019         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38020         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38021         
38022         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38023         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38024         
38025         this.bodyEl.on('click', this.onClick, this);
38026         this.downloadBtn.on('click', this.onDownload, this);
38027         this.trashBtn.on('click', this.onTrash, this);
38028         
38029         this.downloadBtn.hide();
38030         this.trashBtn.hide();
38031         
38032         if(this.showDownload){
38033             this.downloadBtn.show();
38034         }
38035         
38036         if(this.showTrash){
38037             this.trashBtn.show();
38038         }
38039         
38040         if(!this.showDownload && !this.showTrash) {
38041             this.footerEl.hide();
38042         }
38043         
38044     },
38045     
38046     initial : function()
38047     {
38048         this.fireEvent('initial', this);
38049         
38050     },
38051     
38052     onClick : function(e)
38053     {
38054         e.preventDefault();
38055         
38056         this.fireEvent('click', this);
38057     },
38058     
38059     onDownload : function(e)
38060     {
38061         e.preventDefault();
38062         
38063         this.fireEvent('download', this);
38064     },
38065     
38066     onTrash : function(e)
38067     {
38068         e.preventDefault();
38069         
38070         this.fireEvent('trash', this);
38071     }
38072     
38073 });
38074 /*
38075  * - LGPL
38076  *
38077  * FieldLabel
38078  * 
38079  */
38080
38081 /**
38082  * @class Roo.bootstrap.form.FieldLabel
38083  * @extends Roo.bootstrap.Component
38084  * Bootstrap FieldLabel class
38085  * @cfg {String} html contents of the element
38086  * @cfg {String} tag tag of the element default label
38087  * @cfg {String} cls class of the element
38088  * @cfg {String} target label target 
38089  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38090  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38091  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38092  * @cfg {String} iconTooltip default "This field is required"
38093  * @cfg {String} indicatorpos (left|right) default left
38094  * 
38095  * @constructor
38096  * Create a new FieldLabel
38097  * @param {Object} config The config object
38098  */
38099
38100 Roo.bootstrap.form.FieldLabel = function(config){
38101     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38102     
38103     this.addEvents({
38104             /**
38105              * @event invalid
38106              * Fires after the field has been marked as invalid.
38107              * @param {Roo.form.FieldLabel} this
38108              * @param {String} msg The validation message
38109              */
38110             invalid : true,
38111             /**
38112              * @event valid
38113              * Fires after the field has been validated with no errors.
38114              * @param {Roo.form.FieldLabel} this
38115              */
38116             valid : true
38117         });
38118 };
38119
38120 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38121     
38122     tag: 'label',
38123     cls: '',
38124     html: '',
38125     target: '',
38126     allowBlank : true,
38127     invalidClass : 'has-warning',
38128     validClass : 'has-success',
38129     iconTooltip : 'This field is required',
38130     indicatorpos : 'left',
38131     
38132     getAutoCreate : function(){
38133         
38134         var cls = "";
38135         if (!this.allowBlank) {
38136             cls  = "visible";
38137         }
38138         
38139         var cfg = {
38140             tag : this.tag,
38141             cls : 'roo-bootstrap-field-label ' + this.cls,
38142             for : this.target,
38143             cn : [
38144                 {
38145                     tag : 'i',
38146                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38147                     tooltip : this.iconTooltip
38148                 },
38149                 {
38150                     tag : 'span',
38151                     html : this.html
38152                 }
38153             ] 
38154         };
38155         
38156         if(this.indicatorpos == 'right'){
38157             var cfg = {
38158                 tag : this.tag,
38159                 cls : 'roo-bootstrap-field-label ' + this.cls,
38160                 for : this.target,
38161                 cn : [
38162                     {
38163                         tag : 'span',
38164                         html : this.html
38165                     },
38166                     {
38167                         tag : 'i',
38168                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38169                         tooltip : this.iconTooltip
38170                     }
38171                 ] 
38172             };
38173         }
38174         
38175         return cfg;
38176     },
38177     
38178     initEvents: function() 
38179     {
38180         Roo.bootstrap.Element.superclass.initEvents.call(this);
38181         
38182         this.indicator = this.indicatorEl();
38183         
38184         if(this.indicator){
38185             this.indicator.removeClass('visible');
38186             this.indicator.addClass('invisible');
38187         }
38188         
38189         Roo.bootstrap.form.FieldLabel.register(this);
38190     },
38191     
38192     indicatorEl : function()
38193     {
38194         var indicator = this.el.select('i.roo-required-indicator',true).first();
38195         
38196         if(!indicator){
38197             return false;
38198         }
38199         
38200         return indicator;
38201         
38202     },
38203     
38204     /**
38205      * Mark this field as valid
38206      */
38207     markValid : function()
38208     {
38209         if(this.indicator){
38210             this.indicator.removeClass('visible');
38211             this.indicator.addClass('invisible');
38212         }
38213         if (Roo.bootstrap.version == 3) {
38214             this.el.removeClass(this.invalidClass);
38215             this.el.addClass(this.validClass);
38216         } else {
38217             this.el.removeClass('is-invalid');
38218             this.el.addClass('is-valid');
38219         }
38220         
38221         
38222         this.fireEvent('valid', this);
38223     },
38224     
38225     /**
38226      * Mark this field as invalid
38227      * @param {String} msg The validation message
38228      */
38229     markInvalid : function(msg)
38230     {
38231         if(this.indicator){
38232             this.indicator.removeClass('invisible');
38233             this.indicator.addClass('visible');
38234         }
38235           if (Roo.bootstrap.version == 3) {
38236             this.el.removeClass(this.validClass);
38237             this.el.addClass(this.invalidClass);
38238         } else {
38239             this.el.removeClass('is-valid');
38240             this.el.addClass('is-invalid');
38241         }
38242         
38243         
38244         this.fireEvent('invalid', this, msg);
38245     }
38246     
38247    
38248 });
38249
38250 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38251     
38252     groups: {},
38253     
38254      /**
38255     * register a FieldLabel Group
38256     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38257     */
38258     register : function(label)
38259     {
38260         if(this.groups.hasOwnProperty(label.target)){
38261             return;
38262         }
38263      
38264         this.groups[label.target] = label;
38265         
38266     },
38267     /**
38268     * fetch a FieldLabel Group based on the target
38269     * @param {string} target
38270     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38271     */
38272     get: function(target) {
38273         if (typeof(this.groups[target]) == 'undefined') {
38274             return false;
38275         }
38276         
38277         return this.groups[target] ;
38278     }
38279 });
38280
38281  
38282
38283  /*
38284  * - LGPL
38285  *
38286  * page DateSplitField.
38287  * 
38288  */
38289
38290
38291 /**
38292  * @class Roo.bootstrap.form.DateSplitField
38293  * @extends Roo.bootstrap.Component
38294  * Bootstrap DateSplitField class
38295  * @cfg {string} fieldLabel - the label associated
38296  * @cfg {Number} labelWidth set the width of label (0-12)
38297  * @cfg {String} labelAlign (top|left)
38298  * @cfg {Boolean} dayAllowBlank (true|false) default false
38299  * @cfg {Boolean} monthAllowBlank (true|false) default false
38300  * @cfg {Boolean} yearAllowBlank (true|false) default false
38301  * @cfg {string} dayPlaceholder 
38302  * @cfg {string} monthPlaceholder
38303  * @cfg {string} yearPlaceholder
38304  * @cfg {string} dayFormat default 'd'
38305  * @cfg {string} monthFormat default 'm'
38306  * @cfg {string} yearFormat default 'Y'
38307  * @cfg {Number} labellg set the width of label (1-12)
38308  * @cfg {Number} labelmd set the width of label (1-12)
38309  * @cfg {Number} labelsm set the width of label (1-12)
38310  * @cfg {Number} labelxs set the width of label (1-12)
38311
38312  *     
38313  * @constructor
38314  * Create a new DateSplitField
38315  * @param {Object} config The config object
38316  */
38317
38318 Roo.bootstrap.form.DateSplitField = function(config){
38319     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38320     
38321     this.addEvents({
38322         // raw events
38323          /**
38324          * @event years
38325          * getting the data of years
38326          * @param {Roo.bootstrap.form.DateSplitField} this
38327          * @param {Object} years
38328          */
38329         "years" : true,
38330         /**
38331          * @event days
38332          * getting the data of days
38333          * @param {Roo.bootstrap.form.DateSplitField} this
38334          * @param {Object} days
38335          */
38336         "days" : true,
38337         /**
38338          * @event invalid
38339          * Fires after the field has been marked as invalid.
38340          * @param {Roo.form.Field} this
38341          * @param {String} msg The validation message
38342          */
38343         invalid : true,
38344        /**
38345          * @event valid
38346          * Fires after the field has been validated with no errors.
38347          * @param {Roo.form.Field} this
38348          */
38349         valid : true
38350     });
38351 };
38352
38353 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38354     
38355     fieldLabel : '',
38356     labelAlign : 'top',
38357     labelWidth : 3,
38358     dayAllowBlank : false,
38359     monthAllowBlank : false,
38360     yearAllowBlank : false,
38361     dayPlaceholder : '',
38362     monthPlaceholder : '',
38363     yearPlaceholder : '',
38364     dayFormat : 'd',
38365     monthFormat : 'm',
38366     yearFormat : 'Y',
38367     isFormField : true,
38368     labellg : 0,
38369     labelmd : 0,
38370     labelsm : 0,
38371     labelxs : 0,
38372     
38373     getAutoCreate : function()
38374     {
38375         var cfg = {
38376             tag : 'div',
38377             cls : 'row roo-date-split-field-group',
38378             cn : [
38379                 {
38380                     tag : 'input',
38381                     type : 'hidden',
38382                     cls : 'form-hidden-field roo-date-split-field-group-value',
38383                     name : this.name
38384                 }
38385             ]
38386         };
38387         
38388         var labelCls = 'col-md-12';
38389         var contentCls = 'col-md-4';
38390         
38391         if(this.fieldLabel){
38392             
38393             var label = {
38394                 tag : 'div',
38395                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38396                 cn : [
38397                     {
38398                         tag : 'label',
38399                         html : this.fieldLabel
38400                     }
38401                 ]
38402             };
38403             
38404             if(this.labelAlign == 'left'){
38405             
38406                 if(this.labelWidth > 12){
38407                     label.style = "width: " + this.labelWidth + 'px';
38408                 }
38409
38410                 if(this.labelWidth < 13 && this.labelmd == 0){
38411                     this.labelmd = this.labelWidth;
38412                 }
38413
38414                 if(this.labellg > 0){
38415                     labelCls = ' col-lg-' + this.labellg;
38416                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38417                 }
38418
38419                 if(this.labelmd > 0){
38420                     labelCls = ' col-md-' + this.labelmd;
38421                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38422                 }
38423
38424                 if(this.labelsm > 0){
38425                     labelCls = ' col-sm-' + this.labelsm;
38426                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38427                 }
38428
38429                 if(this.labelxs > 0){
38430                     labelCls = ' col-xs-' + this.labelxs;
38431                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38432                 }
38433             }
38434             
38435             label.cls += ' ' + labelCls;
38436             
38437             cfg.cn.push(label);
38438         }
38439         
38440         Roo.each(['day', 'month', 'year'], function(t){
38441             cfg.cn.push({
38442                 tag : 'div',
38443                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38444             });
38445         }, this);
38446         
38447         return cfg;
38448     },
38449     
38450     inputEl: function ()
38451     {
38452         return this.el.select('.roo-date-split-field-group-value', true).first();
38453     },
38454     
38455     onRender : function(ct, position) 
38456     {
38457         var _this = this;
38458         
38459         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38460         
38461         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38462         
38463         this.dayField = new Roo.bootstrap.form.ComboBox({
38464             allowBlank : this.dayAllowBlank,
38465             alwaysQuery : true,
38466             displayField : 'value',
38467             editable : false,
38468             fieldLabel : '',
38469             forceSelection : true,
38470             mode : 'local',
38471             placeholder : this.dayPlaceholder,
38472             selectOnFocus : true,
38473             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38474             triggerAction : 'all',
38475             typeAhead : true,
38476             valueField : 'value',
38477             store : new Roo.data.SimpleStore({
38478                 data : (function() {    
38479                     var days = [];
38480                     _this.fireEvent('days', _this, days);
38481                     return days;
38482                 })(),
38483                 fields : [ 'value' ]
38484             }),
38485             listeners : {
38486                 select : function (_self, record, index)
38487                 {
38488                     _this.setValue(_this.getValue());
38489                 }
38490             }
38491         });
38492
38493         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38494         
38495         this.monthField = new Roo.bootstrap.form.MonthField({
38496             after : '<i class=\"fa fa-calendar\"></i>',
38497             allowBlank : this.monthAllowBlank,
38498             placeholder : this.monthPlaceholder,
38499             readOnly : true,
38500             listeners : {
38501                 render : function (_self)
38502                 {
38503                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38504                         e.preventDefault();
38505                         _self.focus();
38506                     });
38507                 },
38508                 select : function (_self, oldvalue, newvalue)
38509                 {
38510                     _this.setValue(_this.getValue());
38511                 }
38512             }
38513         });
38514         
38515         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38516         
38517         this.yearField = new Roo.bootstrap.form.ComboBox({
38518             allowBlank : this.yearAllowBlank,
38519             alwaysQuery : true,
38520             displayField : 'value',
38521             editable : false,
38522             fieldLabel : '',
38523             forceSelection : true,
38524             mode : 'local',
38525             placeholder : this.yearPlaceholder,
38526             selectOnFocus : true,
38527             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38528             triggerAction : 'all',
38529             typeAhead : true,
38530             valueField : 'value',
38531             store : new Roo.data.SimpleStore({
38532                 data : (function() {
38533                     var years = [];
38534                     _this.fireEvent('years', _this, years);
38535                     return years;
38536                 })(),
38537                 fields : [ 'value' ]
38538             }),
38539             listeners : {
38540                 select : function (_self, record, index)
38541                 {
38542                     _this.setValue(_this.getValue());
38543                 }
38544             }
38545         });
38546
38547         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38548     },
38549     
38550     setValue : function(v, format)
38551     {
38552         this.inputEl.dom.value = v;
38553         
38554         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38555         
38556         var d = Date.parseDate(v, f);
38557         
38558         if(!d){
38559             this.validate();
38560             return;
38561         }
38562         
38563         this.setDay(d.format(this.dayFormat));
38564         this.setMonth(d.format(this.monthFormat));
38565         this.setYear(d.format(this.yearFormat));
38566         
38567         this.validate();
38568         
38569         return;
38570     },
38571     
38572     setDay : function(v)
38573     {
38574         this.dayField.setValue(v);
38575         this.inputEl.dom.value = this.getValue();
38576         this.validate();
38577         return;
38578     },
38579     
38580     setMonth : function(v)
38581     {
38582         this.monthField.setValue(v, true);
38583         this.inputEl.dom.value = this.getValue();
38584         this.validate();
38585         return;
38586     },
38587     
38588     setYear : function(v)
38589     {
38590         this.yearField.setValue(v);
38591         this.inputEl.dom.value = this.getValue();
38592         this.validate();
38593         return;
38594     },
38595     
38596     getDay : function()
38597     {
38598         return this.dayField.getValue();
38599     },
38600     
38601     getMonth : function()
38602     {
38603         return this.monthField.getValue();
38604     },
38605     
38606     getYear : function()
38607     {
38608         return this.yearField.getValue();
38609     },
38610     
38611     getValue : function()
38612     {
38613         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38614         
38615         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38616         
38617         return date;
38618     },
38619     
38620     reset : function()
38621     {
38622         this.setDay('');
38623         this.setMonth('');
38624         this.setYear('');
38625         this.inputEl.dom.value = '';
38626         this.validate();
38627         return;
38628     },
38629     
38630     validate : function()
38631     {
38632         var d = this.dayField.validate();
38633         var m = this.monthField.validate();
38634         var y = this.yearField.validate();
38635         
38636         var valid = true;
38637         
38638         if(
38639                 (!this.dayAllowBlank && !d) ||
38640                 (!this.monthAllowBlank && !m) ||
38641                 (!this.yearAllowBlank && !y)
38642         ){
38643             valid = false;
38644         }
38645         
38646         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38647             return valid;
38648         }
38649         
38650         if(valid){
38651             this.markValid();
38652             return valid;
38653         }
38654         
38655         this.markInvalid();
38656         
38657         return valid;
38658     },
38659     
38660     markValid : function()
38661     {
38662         
38663         var label = this.el.select('label', true).first();
38664         var icon = this.el.select('i.fa-star', true).first();
38665
38666         if(label && icon){
38667             icon.remove();
38668         }
38669         
38670         this.fireEvent('valid', this);
38671     },
38672     
38673      /**
38674      * Mark this field as invalid
38675      * @param {String} msg The validation message
38676      */
38677     markInvalid : function(msg)
38678     {
38679         
38680         var label = this.el.select('label', true).first();
38681         var icon = this.el.select('i.fa-star', true).first();
38682
38683         if(label && !icon){
38684             this.el.select('.roo-date-split-field-label', true).createChild({
38685                 tag : 'i',
38686                 cls : 'text-danger fa fa-lg fa-star',
38687                 tooltip : 'This field is required',
38688                 style : 'margin-right:5px;'
38689             }, label, true);
38690         }
38691         
38692         this.fireEvent('invalid', this, msg);
38693     },
38694     
38695     clearInvalid : function()
38696     {
38697         var label = this.el.select('label', true).first();
38698         var icon = this.el.select('i.fa-star', true).first();
38699
38700         if(label && icon){
38701             icon.remove();
38702         }
38703         
38704         this.fireEvent('valid', this);
38705     },
38706     
38707     getName: function()
38708     {
38709         return this.name;
38710     }
38711     
38712 });
38713
38714  
38715
38716 /**
38717  * @class Roo.bootstrap.LayoutMasonry
38718  * @extends Roo.bootstrap.Component
38719  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38720  * Bootstrap Layout Masonry class
38721  *
38722  * This is based on 
38723  * http://masonry.desandro.com
38724  *
38725  * The idea is to render all the bricks based on vertical width...
38726  *
38727  * The original code extends 'outlayer' - we might need to use that....
38728
38729  * @constructor
38730  * Create a new Element
38731  * @param {Object} config The config object
38732  */
38733
38734 Roo.bootstrap.LayoutMasonry = function(config){
38735     
38736     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38737     
38738     this.bricks = [];
38739     
38740     Roo.bootstrap.LayoutMasonry.register(this);
38741     
38742     this.addEvents({
38743         // raw events
38744         /**
38745          * @event layout
38746          * Fire after layout the items
38747          * @param {Roo.bootstrap.LayoutMasonry} this
38748          * @param {Roo.EventObject} e
38749          */
38750         "layout" : true
38751     });
38752     
38753 };
38754
38755 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38756     
38757     /**
38758      * @cfg {Boolean} isLayoutInstant = no animation?
38759      */   
38760     isLayoutInstant : false, // needed?
38761    
38762     /**
38763      * @cfg {Number} boxWidth  width of the columns
38764      */   
38765     boxWidth : 450,
38766     
38767       /**
38768      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38769      */   
38770     boxHeight : 0,
38771     
38772     /**
38773      * @cfg {Number} padWidth padding below box..
38774      */   
38775     padWidth : 10, 
38776     
38777     /**
38778      * @cfg {Number} gutter gutter width..
38779      */   
38780     gutter : 10,
38781     
38782      /**
38783      * @cfg {Number} maxCols maximum number of columns
38784      */   
38785     
38786     maxCols: 0,
38787     
38788     /**
38789      * @cfg {Boolean} isAutoInitial defalut true
38790      */   
38791     isAutoInitial : true, 
38792     
38793     containerWidth: 0,
38794     
38795     /**
38796      * @cfg {Boolean} isHorizontal defalut false
38797      */   
38798     isHorizontal : false, 
38799
38800     currentSize : null,
38801     
38802     tag: 'div',
38803     
38804     cls: '',
38805     
38806     bricks: null, //CompositeElement
38807     
38808     cols : 1,
38809     
38810     _isLayoutInited : false,
38811     
38812 //    isAlternative : false, // only use for vertical layout...
38813     
38814     /**
38815      * @cfg {Number} alternativePadWidth padding below box..
38816      */   
38817     alternativePadWidth : 50,
38818     
38819     selectedBrick : [],
38820     
38821     getAutoCreate : function(){
38822         
38823         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38824         
38825         var cfg = {
38826             tag: this.tag,
38827             cls: 'blog-masonary-wrapper ' + this.cls,
38828             cn : {
38829                 cls : 'mas-boxes masonary'
38830             }
38831         };
38832         
38833         return cfg;
38834     },
38835     
38836     getChildContainer: function( )
38837     {
38838         if (this.boxesEl) {
38839             return this.boxesEl;
38840         }
38841         
38842         this.boxesEl = this.el.select('.mas-boxes').first();
38843         
38844         return this.boxesEl;
38845     },
38846     
38847     
38848     initEvents : function()
38849     {
38850         var _this = this;
38851         
38852         if(this.isAutoInitial){
38853             Roo.log('hook children rendered');
38854             this.on('childrenrendered', function() {
38855                 Roo.log('children rendered');
38856                 _this.initial();
38857             } ,this);
38858         }
38859     },
38860     
38861     initial : function()
38862     {
38863         this.selectedBrick = [];
38864         
38865         this.currentSize = this.el.getBox(true);
38866         
38867         Roo.EventManager.onWindowResize(this.resize, this); 
38868
38869         if(!this.isAutoInitial){
38870             this.layout();
38871             return;
38872         }
38873         
38874         this.layout();
38875         
38876         return;
38877         //this.layout.defer(500,this);
38878         
38879     },
38880     
38881     resize : function()
38882     {
38883         var cs = this.el.getBox(true);
38884         
38885         if (
38886                 this.currentSize.width == cs.width && 
38887                 this.currentSize.x == cs.x && 
38888                 this.currentSize.height == cs.height && 
38889                 this.currentSize.y == cs.y 
38890         ) {
38891             Roo.log("no change in with or X or Y");
38892             return;
38893         }
38894         
38895         this.currentSize = cs;
38896         
38897         this.layout();
38898         
38899     },
38900     
38901     layout : function()
38902     {   
38903         this._resetLayout();
38904         
38905         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38906         
38907         this.layoutItems( isInstant );
38908       
38909         this._isLayoutInited = true;
38910         
38911         this.fireEvent('layout', this);
38912         
38913     },
38914     
38915     _resetLayout : function()
38916     {
38917         if(this.isHorizontal){
38918             this.horizontalMeasureColumns();
38919             return;
38920         }
38921         
38922         this.verticalMeasureColumns();
38923         
38924     },
38925     
38926     verticalMeasureColumns : function()
38927     {
38928         this.getContainerWidth();
38929         
38930 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38931 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38932 //            return;
38933 //        }
38934         
38935         var boxWidth = this.boxWidth + this.padWidth;
38936         
38937         if(this.containerWidth < this.boxWidth){
38938             boxWidth = this.containerWidth
38939         }
38940         
38941         var containerWidth = this.containerWidth;
38942         
38943         var cols = Math.floor(containerWidth / boxWidth);
38944         
38945         this.cols = Math.max( cols, 1 );
38946         
38947         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38948         
38949         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38950         
38951         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38952         
38953         this.colWidth = boxWidth + avail - this.padWidth;
38954         
38955         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38956         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38957     },
38958     
38959     horizontalMeasureColumns : function()
38960     {
38961         this.getContainerWidth();
38962         
38963         var boxWidth = this.boxWidth;
38964         
38965         if(this.containerWidth < boxWidth){
38966             boxWidth = this.containerWidth;
38967         }
38968         
38969         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38970         
38971         this.el.setHeight(boxWidth);
38972         
38973     },
38974     
38975     getContainerWidth : function()
38976     {
38977         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38978     },
38979     
38980     layoutItems : function( isInstant )
38981     {
38982         Roo.log(this.bricks);
38983         
38984         var items = Roo.apply([], this.bricks);
38985         
38986         if(this.isHorizontal){
38987             this._horizontalLayoutItems( items , isInstant );
38988             return;
38989         }
38990         
38991 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38992 //            this._verticalAlternativeLayoutItems( items , isInstant );
38993 //            return;
38994 //        }
38995         
38996         this._verticalLayoutItems( items , isInstant );
38997         
38998     },
38999     
39000     _verticalLayoutItems : function ( items , isInstant)
39001     {
39002         if ( !items || !items.length ) {
39003             return;
39004         }
39005         
39006         var standard = [
39007             ['xs', 'xs', 'xs', 'tall'],
39008             ['xs', 'xs', 'tall'],
39009             ['xs', 'xs', 'sm'],
39010             ['xs', 'xs', 'xs'],
39011             ['xs', 'tall'],
39012             ['xs', 'sm'],
39013             ['xs', 'xs'],
39014             ['xs'],
39015             
39016             ['sm', 'xs', 'xs'],
39017             ['sm', 'xs'],
39018             ['sm'],
39019             
39020             ['tall', 'xs', 'xs', 'xs'],
39021             ['tall', 'xs', 'xs'],
39022             ['tall', 'xs'],
39023             ['tall']
39024             
39025         ];
39026         
39027         var queue = [];
39028         
39029         var boxes = [];
39030         
39031         var box = [];
39032         
39033         Roo.each(items, function(item, k){
39034             
39035             switch (item.size) {
39036                 // these layouts take up a full box,
39037                 case 'md' :
39038                 case 'md-left' :
39039                 case 'md-right' :
39040                 case 'wide' :
39041                     
39042                     if(box.length){
39043                         boxes.push(box);
39044                         box = [];
39045                     }
39046                     
39047                     boxes.push([item]);
39048                     
39049                     break;
39050                     
39051                 case 'xs' :
39052                 case 'sm' :
39053                 case 'tall' :
39054                     
39055                     box.push(item);
39056                     
39057                     break;
39058                 default :
39059                     break;
39060                     
39061             }
39062             
39063         }, this);
39064         
39065         if(box.length){
39066             boxes.push(box);
39067             box = [];
39068         }
39069         
39070         var filterPattern = function(box, length)
39071         {
39072             if(!box.length){
39073                 return;
39074             }
39075             
39076             var match = false;
39077             
39078             var pattern = box.slice(0, length);
39079             
39080             var format = [];
39081             
39082             Roo.each(pattern, function(i){
39083                 format.push(i.size);
39084             }, this);
39085             
39086             Roo.each(standard, function(s){
39087                 
39088                 if(String(s) != String(format)){
39089                     return;
39090                 }
39091                 
39092                 match = true;
39093                 return false;
39094                 
39095             }, this);
39096             
39097             if(!match && length == 1){
39098                 return;
39099             }
39100             
39101             if(!match){
39102                 filterPattern(box, length - 1);
39103                 return;
39104             }
39105                 
39106             queue.push(pattern);
39107
39108             box = box.slice(length, box.length);
39109
39110             filterPattern(box, 4);
39111
39112             return;
39113             
39114         }
39115         
39116         Roo.each(boxes, function(box, k){
39117             
39118             if(!box.length){
39119                 return;
39120             }
39121             
39122             if(box.length == 1){
39123                 queue.push(box);
39124                 return;
39125             }
39126             
39127             filterPattern(box, 4);
39128             
39129         }, this);
39130         
39131         this._processVerticalLayoutQueue( queue, isInstant );
39132         
39133     },
39134     
39135 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39136 //    {
39137 //        if ( !items || !items.length ) {
39138 //            return;
39139 //        }
39140 //
39141 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39142 //        
39143 //    },
39144     
39145     _horizontalLayoutItems : function ( items , isInstant)
39146     {
39147         if ( !items || !items.length || items.length < 3) {
39148             return;
39149         }
39150         
39151         items.reverse();
39152         
39153         var eItems = items.slice(0, 3);
39154         
39155         items = items.slice(3, items.length);
39156         
39157         var standard = [
39158             ['xs', 'xs', 'xs', 'wide'],
39159             ['xs', 'xs', 'wide'],
39160             ['xs', 'xs', 'sm'],
39161             ['xs', 'xs', 'xs'],
39162             ['xs', 'wide'],
39163             ['xs', 'sm'],
39164             ['xs', 'xs'],
39165             ['xs'],
39166             
39167             ['sm', 'xs', 'xs'],
39168             ['sm', 'xs'],
39169             ['sm'],
39170             
39171             ['wide', 'xs', 'xs', 'xs'],
39172             ['wide', 'xs', 'xs'],
39173             ['wide', 'xs'],
39174             ['wide'],
39175             
39176             ['wide-thin']
39177         ];
39178         
39179         var queue = [];
39180         
39181         var boxes = [];
39182         
39183         var box = [];
39184         
39185         Roo.each(items, function(item, k){
39186             
39187             switch (item.size) {
39188                 case 'md' :
39189                 case 'md-left' :
39190                 case 'md-right' :
39191                 case 'tall' :
39192                     
39193                     if(box.length){
39194                         boxes.push(box);
39195                         box = [];
39196                     }
39197                     
39198                     boxes.push([item]);
39199                     
39200                     break;
39201                     
39202                 case 'xs' :
39203                 case 'sm' :
39204                 case 'wide' :
39205                 case 'wide-thin' :
39206                     
39207                     box.push(item);
39208                     
39209                     break;
39210                 default :
39211                     break;
39212                     
39213             }
39214             
39215         }, this);
39216         
39217         if(box.length){
39218             boxes.push(box);
39219             box = [];
39220         }
39221         
39222         var filterPattern = function(box, length)
39223         {
39224             if(!box.length){
39225                 return;
39226             }
39227             
39228             var match = false;
39229             
39230             var pattern = box.slice(0, length);
39231             
39232             var format = [];
39233             
39234             Roo.each(pattern, function(i){
39235                 format.push(i.size);
39236             }, this);
39237             
39238             Roo.each(standard, function(s){
39239                 
39240                 if(String(s) != String(format)){
39241                     return;
39242                 }
39243                 
39244                 match = true;
39245                 return false;
39246                 
39247             }, this);
39248             
39249             if(!match && length == 1){
39250                 return;
39251             }
39252             
39253             if(!match){
39254                 filterPattern(box, length - 1);
39255                 return;
39256             }
39257                 
39258             queue.push(pattern);
39259
39260             box = box.slice(length, box.length);
39261
39262             filterPattern(box, 4);
39263
39264             return;
39265             
39266         }
39267         
39268         Roo.each(boxes, function(box, k){
39269             
39270             if(!box.length){
39271                 return;
39272             }
39273             
39274             if(box.length == 1){
39275                 queue.push(box);
39276                 return;
39277             }
39278             
39279             filterPattern(box, 4);
39280             
39281         }, this);
39282         
39283         
39284         var prune = [];
39285         
39286         var pos = this.el.getBox(true);
39287         
39288         var minX = pos.x;
39289         
39290         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39291         
39292         var hit_end = false;
39293         
39294         Roo.each(queue, function(box){
39295             
39296             if(hit_end){
39297                 
39298                 Roo.each(box, function(b){
39299                 
39300                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39301                     b.el.hide();
39302
39303                 }, this);
39304
39305                 return;
39306             }
39307             
39308             var mx = 0;
39309             
39310             Roo.each(box, function(b){
39311                 
39312                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39313                 b.el.show();
39314
39315                 mx = Math.max(mx, b.x);
39316                 
39317             }, this);
39318             
39319             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39320             
39321             if(maxX < minX){
39322                 
39323                 Roo.each(box, function(b){
39324                 
39325                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39326                     b.el.hide();
39327                     
39328                 }, this);
39329                 
39330                 hit_end = true;
39331                 
39332                 return;
39333             }
39334             
39335             prune.push(box);
39336             
39337         }, this);
39338         
39339         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39340     },
39341     
39342     /** Sets position of item in DOM
39343     * @param {Element} item
39344     * @param {Number} x - horizontal position
39345     * @param {Number} y - vertical position
39346     * @param {Boolean} isInstant - disables transitions
39347     */
39348     _processVerticalLayoutQueue : function( queue, isInstant )
39349     {
39350         var pos = this.el.getBox(true);
39351         var x = pos.x;
39352         var y = pos.y;
39353         var maxY = [];
39354         
39355         for (var i = 0; i < this.cols; i++){
39356             maxY[i] = pos.y;
39357         }
39358         
39359         Roo.each(queue, function(box, k){
39360             
39361             var col = k % this.cols;
39362             
39363             Roo.each(box, function(b,kk){
39364                 
39365                 b.el.position('absolute');
39366                 
39367                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39368                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39369                 
39370                 if(b.size == 'md-left' || b.size == 'md-right'){
39371                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39372                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39373                 }
39374                 
39375                 b.el.setWidth(width);
39376                 b.el.setHeight(height);
39377                 // iframe?
39378                 b.el.select('iframe',true).setSize(width,height);
39379                 
39380             }, this);
39381             
39382             for (var i = 0; i < this.cols; i++){
39383                 
39384                 if(maxY[i] < maxY[col]){
39385                     col = i;
39386                     continue;
39387                 }
39388                 
39389                 col = Math.min(col, i);
39390                 
39391             }
39392             
39393             x = pos.x + col * (this.colWidth + this.padWidth);
39394             
39395             y = maxY[col];
39396             
39397             var positions = [];
39398             
39399             switch (box.length){
39400                 case 1 :
39401                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39402                     break;
39403                 case 2 :
39404                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39405                     break;
39406                 case 3 :
39407                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39408                     break;
39409                 case 4 :
39410                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39411                     break;
39412                 default :
39413                     break;
39414             }
39415             
39416             Roo.each(box, function(b,kk){
39417                 
39418                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39419                 
39420                 var sz = b.el.getSize();
39421                 
39422                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39423                 
39424             }, this);
39425             
39426         }, this);
39427         
39428         var mY = 0;
39429         
39430         for (var i = 0; i < this.cols; i++){
39431             mY = Math.max(mY, maxY[i]);
39432         }
39433         
39434         this.el.setHeight(mY - pos.y);
39435         
39436     },
39437     
39438 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39439 //    {
39440 //        var pos = this.el.getBox(true);
39441 //        var x = pos.x;
39442 //        var y = pos.y;
39443 //        var maxX = pos.right;
39444 //        
39445 //        var maxHeight = 0;
39446 //        
39447 //        Roo.each(items, function(item, k){
39448 //            
39449 //            var c = k % 2;
39450 //            
39451 //            item.el.position('absolute');
39452 //                
39453 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39454 //
39455 //            item.el.setWidth(width);
39456 //
39457 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39458 //
39459 //            item.el.setHeight(height);
39460 //            
39461 //            if(c == 0){
39462 //                item.el.setXY([x, y], isInstant ? false : true);
39463 //            } else {
39464 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39465 //            }
39466 //            
39467 //            y = y + height + this.alternativePadWidth;
39468 //            
39469 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39470 //            
39471 //        }, this);
39472 //        
39473 //        this.el.setHeight(maxHeight);
39474 //        
39475 //    },
39476     
39477     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39478     {
39479         var pos = this.el.getBox(true);
39480         
39481         var minX = pos.x;
39482         var minY = pos.y;
39483         
39484         var maxX = pos.right;
39485         
39486         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39487         
39488         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39489         
39490         Roo.each(queue, function(box, k){
39491             
39492             Roo.each(box, function(b, kk){
39493                 
39494                 b.el.position('absolute');
39495                 
39496                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39497                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39498                 
39499                 if(b.size == 'md-left' || b.size == 'md-right'){
39500                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39501                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39502                 }
39503                 
39504                 b.el.setWidth(width);
39505                 b.el.setHeight(height);
39506                 
39507             }, this);
39508             
39509             if(!box.length){
39510                 return;
39511             }
39512             
39513             var positions = [];
39514             
39515             switch (box.length){
39516                 case 1 :
39517                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39518                     break;
39519                 case 2 :
39520                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39521                     break;
39522                 case 3 :
39523                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39524                     break;
39525                 case 4 :
39526                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39527                     break;
39528                 default :
39529                     break;
39530             }
39531             
39532             Roo.each(box, function(b,kk){
39533                 
39534                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39535                 
39536                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39537                 
39538             }, this);
39539             
39540         }, this);
39541         
39542     },
39543     
39544     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39545     {
39546         Roo.each(eItems, function(b,k){
39547             
39548             b.size = (k == 0) ? 'sm' : 'xs';
39549             b.x = (k == 0) ? 2 : 1;
39550             b.y = (k == 0) ? 2 : 1;
39551             
39552             b.el.position('absolute');
39553             
39554             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39555                 
39556             b.el.setWidth(width);
39557             
39558             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39559             
39560             b.el.setHeight(height);
39561             
39562         }, this);
39563
39564         var positions = [];
39565         
39566         positions.push({
39567             x : maxX - this.unitWidth * 2 - this.gutter,
39568             y : minY
39569         });
39570         
39571         positions.push({
39572             x : maxX - this.unitWidth,
39573             y : minY + (this.unitWidth + this.gutter) * 2
39574         });
39575         
39576         positions.push({
39577             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39578             y : minY
39579         });
39580         
39581         Roo.each(eItems, function(b,k){
39582             
39583             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39584
39585         }, this);
39586         
39587     },
39588     
39589     getVerticalOneBoxColPositions : function(x, y, box)
39590     {
39591         var pos = [];
39592         
39593         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39594         
39595         if(box[0].size == 'md-left'){
39596             rand = 0;
39597         }
39598         
39599         if(box[0].size == 'md-right'){
39600             rand = 1;
39601         }
39602         
39603         pos.push({
39604             x : x + (this.unitWidth + this.gutter) * rand,
39605             y : y
39606         });
39607         
39608         return pos;
39609     },
39610     
39611     getVerticalTwoBoxColPositions : function(x, y, box)
39612     {
39613         var pos = [];
39614         
39615         if(box[0].size == 'xs'){
39616             
39617             pos.push({
39618                 x : x,
39619                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39620             });
39621
39622             pos.push({
39623                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39624                 y : y
39625             });
39626             
39627             return pos;
39628             
39629         }
39630         
39631         pos.push({
39632             x : x,
39633             y : y
39634         });
39635
39636         pos.push({
39637             x : x + (this.unitWidth + this.gutter) * 2,
39638             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39639         });
39640         
39641         return pos;
39642         
39643     },
39644     
39645     getVerticalThreeBoxColPositions : function(x, y, box)
39646     {
39647         var pos = [];
39648         
39649         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39650             
39651             pos.push({
39652                 x : x,
39653                 y : y
39654             });
39655
39656             pos.push({
39657                 x : x + (this.unitWidth + this.gutter) * 1,
39658                 y : y
39659             });
39660             
39661             pos.push({
39662                 x : x + (this.unitWidth + this.gutter) * 2,
39663                 y : y
39664             });
39665             
39666             return pos;
39667             
39668         }
39669         
39670         if(box[0].size == 'xs' && box[1].size == 'xs'){
39671             
39672             pos.push({
39673                 x : x,
39674                 y : y
39675             });
39676
39677             pos.push({
39678                 x : x,
39679                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39680             });
39681             
39682             pos.push({
39683                 x : x + (this.unitWidth + this.gutter) * 1,
39684                 y : y
39685             });
39686             
39687             return pos;
39688             
39689         }
39690         
39691         pos.push({
39692             x : x,
39693             y : y
39694         });
39695
39696         pos.push({
39697             x : x + (this.unitWidth + this.gutter) * 2,
39698             y : y
39699         });
39700
39701         pos.push({
39702             x : x + (this.unitWidth + this.gutter) * 2,
39703             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39704         });
39705             
39706         return pos;
39707         
39708     },
39709     
39710     getVerticalFourBoxColPositions : function(x, y, box)
39711     {
39712         var pos = [];
39713         
39714         if(box[0].size == 'xs'){
39715             
39716             pos.push({
39717                 x : x,
39718                 y : y
39719             });
39720
39721             pos.push({
39722                 x : x,
39723                 y : y + (this.unitHeight + this.gutter) * 1
39724             });
39725             
39726             pos.push({
39727                 x : x,
39728                 y : y + (this.unitHeight + this.gutter) * 2
39729             });
39730             
39731             pos.push({
39732                 x : x + (this.unitWidth + this.gutter) * 1,
39733                 y : y
39734             });
39735             
39736             return pos;
39737             
39738         }
39739         
39740         pos.push({
39741             x : x,
39742             y : y
39743         });
39744
39745         pos.push({
39746             x : x + (this.unitWidth + this.gutter) * 2,
39747             y : y
39748         });
39749
39750         pos.push({
39751             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39752             y : y + (this.unitHeight + this.gutter) * 1
39753         });
39754
39755         pos.push({
39756             x : x + (this.unitWidth + this.gutter) * 2,
39757             y : y + (this.unitWidth + this.gutter) * 2
39758         });
39759
39760         return pos;
39761         
39762     },
39763     
39764     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39765     {
39766         var pos = [];
39767         
39768         if(box[0].size == 'md-left'){
39769             pos.push({
39770                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39771                 y : minY
39772             });
39773             
39774             return pos;
39775         }
39776         
39777         if(box[0].size == 'md-right'){
39778             pos.push({
39779                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39780                 y : minY + (this.unitWidth + this.gutter) * 1
39781             });
39782             
39783             return pos;
39784         }
39785         
39786         var rand = Math.floor(Math.random() * (4 - box[0].y));
39787         
39788         pos.push({
39789             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39790             y : minY + (this.unitWidth + this.gutter) * rand
39791         });
39792         
39793         return pos;
39794         
39795     },
39796     
39797     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39798     {
39799         var pos = [];
39800         
39801         if(box[0].size == 'xs'){
39802             
39803             pos.push({
39804                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39805                 y : minY
39806             });
39807
39808             pos.push({
39809                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39810                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39811             });
39812             
39813             return pos;
39814             
39815         }
39816         
39817         pos.push({
39818             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39819             y : minY
39820         });
39821
39822         pos.push({
39823             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39824             y : minY + (this.unitWidth + this.gutter) * 2
39825         });
39826         
39827         return pos;
39828         
39829     },
39830     
39831     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39832     {
39833         var pos = [];
39834         
39835         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39836             
39837             pos.push({
39838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39839                 y : minY
39840             });
39841
39842             pos.push({
39843                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39844                 y : minY + (this.unitWidth + this.gutter) * 1
39845             });
39846             
39847             pos.push({
39848                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39849                 y : minY + (this.unitWidth + this.gutter) * 2
39850             });
39851             
39852             return pos;
39853             
39854         }
39855         
39856         if(box[0].size == 'xs' && box[1].size == 'xs'){
39857             
39858             pos.push({
39859                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39860                 y : minY
39861             });
39862
39863             pos.push({
39864                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39865                 y : minY
39866             });
39867             
39868             pos.push({
39869                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39870                 y : minY + (this.unitWidth + this.gutter) * 1
39871             });
39872             
39873             return pos;
39874             
39875         }
39876         
39877         pos.push({
39878             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39879             y : minY
39880         });
39881
39882         pos.push({
39883             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39884             y : minY + (this.unitWidth + this.gutter) * 2
39885         });
39886
39887         pos.push({
39888             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39889             y : minY + (this.unitWidth + this.gutter) * 2
39890         });
39891             
39892         return pos;
39893         
39894     },
39895     
39896     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39897     {
39898         var pos = [];
39899         
39900         if(box[0].size == 'xs'){
39901             
39902             pos.push({
39903                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39904                 y : minY
39905             });
39906
39907             pos.push({
39908                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39909                 y : minY
39910             });
39911             
39912             pos.push({
39913                 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),
39914                 y : minY
39915             });
39916             
39917             pos.push({
39918                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39919                 y : minY + (this.unitWidth + this.gutter) * 1
39920             });
39921             
39922             return pos;
39923             
39924         }
39925         
39926         pos.push({
39927             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39928             y : minY
39929         });
39930         
39931         pos.push({
39932             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39933             y : minY + (this.unitWidth + this.gutter) * 2
39934         });
39935         
39936         pos.push({
39937             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39938             y : minY + (this.unitWidth + this.gutter) * 2
39939         });
39940         
39941         pos.push({
39942             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),
39943             y : minY + (this.unitWidth + this.gutter) * 2
39944         });
39945
39946         return pos;
39947         
39948     },
39949     
39950     /**
39951     * remove a Masonry Brick
39952     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39953     */
39954     removeBrick : function(brick_id)
39955     {
39956         if (!brick_id) {
39957             return;
39958         }
39959         
39960         for (var i = 0; i<this.bricks.length; i++) {
39961             if (this.bricks[i].id == brick_id) {
39962                 this.bricks.splice(i,1);
39963                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39964                 this.initial();
39965             }
39966         }
39967     },
39968     
39969     /**
39970     * adds a Masonry Brick
39971     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39972     */
39973     addBrick : function(cfg)
39974     {
39975         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39976         //this.register(cn);
39977         cn.parentId = this.id;
39978         cn.render(this.el);
39979         return cn;
39980     },
39981     
39982     /**
39983     * register a Masonry Brick
39984     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39985     */
39986     
39987     register : function(brick)
39988     {
39989         this.bricks.push(brick);
39990         brick.masonryId = this.id;
39991     },
39992     
39993     /**
39994     * clear all the Masonry Brick
39995     */
39996     clearAll : function()
39997     {
39998         this.bricks = [];
39999         //this.getChildContainer().dom.innerHTML = "";
40000         this.el.dom.innerHTML = '';
40001     },
40002     
40003     getSelected : function()
40004     {
40005         if (!this.selectedBrick) {
40006             return false;
40007         }
40008         
40009         return this.selectedBrick;
40010     }
40011 });
40012
40013 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40014     
40015     groups: {},
40016      /**
40017     * register a Masonry Layout
40018     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40019     */
40020     
40021     register : function(layout)
40022     {
40023         this.groups[layout.id] = layout;
40024     },
40025     /**
40026     * fetch a  Masonry Layout based on the masonry layout ID
40027     * @param {string} the masonry layout to add
40028     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40029     */
40030     
40031     get: function(layout_id) {
40032         if (typeof(this.groups[layout_id]) == 'undefined') {
40033             return false;
40034         }
40035         return this.groups[layout_id] ;
40036     }
40037     
40038     
40039     
40040 });
40041
40042  
40043
40044  /**
40045  *
40046  * This is based on 
40047  * http://masonry.desandro.com
40048  *
40049  * The idea is to render all the bricks based on vertical width...
40050  *
40051  * The original code extends 'outlayer' - we might need to use that....
40052  * 
40053  */
40054
40055
40056 /**
40057  * @class Roo.bootstrap.LayoutMasonryAuto
40058  * @extends Roo.bootstrap.Component
40059  * Bootstrap Layout Masonry class
40060  * 
40061  * @constructor
40062  * Create a new Element
40063  * @param {Object} config The config object
40064  */
40065
40066 Roo.bootstrap.LayoutMasonryAuto = function(config){
40067     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40068 };
40069
40070 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40071     
40072       /**
40073      * @cfg {Boolean} isFitWidth  - resize the width..
40074      */   
40075     isFitWidth : false,  // options..
40076     /**
40077      * @cfg {Boolean} isOriginLeft = left align?
40078      */   
40079     isOriginLeft : true,
40080     /**
40081      * @cfg {Boolean} isOriginTop = top align?
40082      */   
40083     isOriginTop : false,
40084     /**
40085      * @cfg {Boolean} isLayoutInstant = no animation?
40086      */   
40087     isLayoutInstant : false, // needed?
40088     /**
40089      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40090      */   
40091     isResizingContainer : true,
40092     /**
40093      * @cfg {Number} columnWidth  width of the columns 
40094      */   
40095     
40096     columnWidth : 0,
40097     
40098     /**
40099      * @cfg {Number} maxCols maximum number of columns
40100      */   
40101     
40102     maxCols: 0,
40103     /**
40104      * @cfg {Number} padHeight padding below box..
40105      */   
40106     
40107     padHeight : 10, 
40108     
40109     /**
40110      * @cfg {Boolean} isAutoInitial defalut true
40111      */   
40112     
40113     isAutoInitial : true, 
40114     
40115     // private?
40116     gutter : 0,
40117     
40118     containerWidth: 0,
40119     initialColumnWidth : 0,
40120     currentSize : null,
40121     
40122     colYs : null, // array.
40123     maxY : 0,
40124     padWidth: 10,
40125     
40126     
40127     tag: 'div',
40128     cls: '',
40129     bricks: null, //CompositeElement
40130     cols : 0, // array?
40131     // element : null, // wrapped now this.el
40132     _isLayoutInited : null, 
40133     
40134     
40135     getAutoCreate : function(){
40136         
40137         var cfg = {
40138             tag: this.tag,
40139             cls: 'blog-masonary-wrapper ' + this.cls,
40140             cn : {
40141                 cls : 'mas-boxes masonary'
40142             }
40143         };
40144         
40145         return cfg;
40146     },
40147     
40148     getChildContainer: function( )
40149     {
40150         if (this.boxesEl) {
40151             return this.boxesEl;
40152         }
40153         
40154         this.boxesEl = this.el.select('.mas-boxes').first();
40155         
40156         return this.boxesEl;
40157     },
40158     
40159     
40160     initEvents : function()
40161     {
40162         var _this = this;
40163         
40164         if(this.isAutoInitial){
40165             Roo.log('hook children rendered');
40166             this.on('childrenrendered', function() {
40167                 Roo.log('children rendered');
40168                 _this.initial();
40169             } ,this);
40170         }
40171         
40172     },
40173     
40174     initial : function()
40175     {
40176         this.reloadItems();
40177
40178         this.currentSize = this.el.getBox(true);
40179
40180         /// was window resize... - let's see if this works..
40181         Roo.EventManager.onWindowResize(this.resize, this); 
40182
40183         if(!this.isAutoInitial){
40184             this.layout();
40185             return;
40186         }
40187         
40188         this.layout.defer(500,this);
40189     },
40190     
40191     reloadItems: function()
40192     {
40193         this.bricks = this.el.select('.masonry-brick', true);
40194         
40195         this.bricks.each(function(b) {
40196             //Roo.log(b.getSize());
40197             if (!b.attr('originalwidth')) {
40198                 b.attr('originalwidth',  b.getSize().width);
40199             }
40200             
40201         });
40202         
40203         Roo.log(this.bricks.elements.length);
40204     },
40205     
40206     resize : function()
40207     {
40208         Roo.log('resize');
40209         var cs = this.el.getBox(true);
40210         
40211         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40212             Roo.log("no change in with or X");
40213             return;
40214         }
40215         this.currentSize = cs;
40216         this.layout();
40217     },
40218     
40219     layout : function()
40220     {
40221          Roo.log('layout');
40222         this._resetLayout();
40223         //this._manageStamps();
40224       
40225         // don't animate first layout
40226         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40227         this.layoutItems( isInstant );
40228       
40229         // flag for initalized
40230         this._isLayoutInited = true;
40231     },
40232     
40233     layoutItems : function( isInstant )
40234     {
40235         //var items = this._getItemsForLayout( this.items );
40236         // original code supports filtering layout items.. we just ignore it..
40237         
40238         this._layoutItems( this.bricks , isInstant );
40239       
40240         this._postLayout();
40241     },
40242     _layoutItems : function ( items , isInstant)
40243     {
40244        //this.fireEvent( 'layout', this, items );
40245     
40246
40247         if ( !items || !items.elements.length ) {
40248           // no items, emit event with empty array
40249             return;
40250         }
40251
40252         var queue = [];
40253         items.each(function(item) {
40254             Roo.log("layout item");
40255             Roo.log(item);
40256             // get x/y object from method
40257             var position = this._getItemLayoutPosition( item );
40258             // enqueue
40259             position.item = item;
40260             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40261             queue.push( position );
40262         }, this);
40263       
40264         this._processLayoutQueue( queue );
40265     },
40266     /** Sets position of item in DOM
40267     * @param {Element} item
40268     * @param {Number} x - horizontal position
40269     * @param {Number} y - vertical position
40270     * @param {Boolean} isInstant - disables transitions
40271     */
40272     _processLayoutQueue : function( queue )
40273     {
40274         for ( var i=0, len = queue.length; i < len; i++ ) {
40275             var obj = queue[i];
40276             obj.item.position('absolute');
40277             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40278         }
40279     },
40280       
40281     
40282     /**
40283     * Any logic you want to do after each layout,
40284     * i.e. size the container
40285     */
40286     _postLayout : function()
40287     {
40288         this.resizeContainer();
40289     },
40290     
40291     resizeContainer : function()
40292     {
40293         if ( !this.isResizingContainer ) {
40294             return;
40295         }
40296         var size = this._getContainerSize();
40297         if ( size ) {
40298             this.el.setSize(size.width,size.height);
40299             this.boxesEl.setSize(size.width,size.height);
40300         }
40301     },
40302     
40303     
40304     
40305     _resetLayout : function()
40306     {
40307         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40308         this.colWidth = this.el.getWidth();
40309         //this.gutter = this.el.getWidth(); 
40310         
40311         this.measureColumns();
40312
40313         // reset column Y
40314         var i = this.cols;
40315         this.colYs = [];
40316         while (i--) {
40317             this.colYs.push( 0 );
40318         }
40319     
40320         this.maxY = 0;
40321     },
40322
40323     measureColumns : function()
40324     {
40325         this.getContainerWidth();
40326       // if columnWidth is 0, default to outerWidth of first item
40327         if ( !this.columnWidth ) {
40328             var firstItem = this.bricks.first();
40329             Roo.log(firstItem);
40330             this.columnWidth  = this.containerWidth;
40331             if (firstItem && firstItem.attr('originalwidth') ) {
40332                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40333             }
40334             // columnWidth fall back to item of first element
40335             Roo.log("set column width?");
40336                         this.initialColumnWidth = this.columnWidth  ;
40337
40338             // if first elem has no width, default to size of container
40339             
40340         }
40341         
40342         
40343         if (this.initialColumnWidth) {
40344             this.columnWidth = this.initialColumnWidth;
40345         }
40346         
40347         
40348             
40349         // column width is fixed at the top - however if container width get's smaller we should
40350         // reduce it...
40351         
40352         // this bit calcs how man columns..
40353             
40354         var columnWidth = this.columnWidth += this.gutter;
40355       
40356         // calculate columns
40357         var containerWidth = this.containerWidth + this.gutter;
40358         
40359         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40360         // fix rounding errors, typically with gutters
40361         var excess = columnWidth - containerWidth % columnWidth;
40362         
40363         
40364         // if overshoot is less than a pixel, round up, otherwise floor it
40365         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40366         cols = Math[ mathMethod ]( cols );
40367         this.cols = Math.max( cols, 1 );
40368         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40369         
40370          // padding positioning..
40371         var totalColWidth = this.cols * this.columnWidth;
40372         var padavail = this.containerWidth - totalColWidth;
40373         // so for 2 columns - we need 3 'pads'
40374         
40375         var padNeeded = (1+this.cols) * this.padWidth;
40376         
40377         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40378         
40379         this.columnWidth += padExtra
40380         //this.padWidth = Math.floor(padavail /  ( this.cols));
40381         
40382         // adjust colum width so that padding is fixed??
40383         
40384         // we have 3 columns ... total = width * 3
40385         // we have X left over... that should be used by 
40386         
40387         //if (this.expandC) {
40388             
40389         //}
40390         
40391         
40392         
40393     },
40394     
40395     getContainerWidth : function()
40396     {
40397        /* // container is parent if fit width
40398         var container = this.isFitWidth ? this.element.parentNode : this.element;
40399         // check that this.size and size are there
40400         // IE8 triggers resize on body size change, so they might not be
40401         
40402         var size = getSize( container );  //FIXME
40403         this.containerWidth = size && size.innerWidth; //FIXME
40404         */
40405          
40406         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40407         
40408     },
40409     
40410     _getItemLayoutPosition : function( item )  // what is item?
40411     {
40412         // we resize the item to our columnWidth..
40413       
40414         item.setWidth(this.columnWidth);
40415         item.autoBoxAdjust  = false;
40416         
40417         var sz = item.getSize();
40418  
40419         // how many columns does this brick span
40420         var remainder = this.containerWidth % this.columnWidth;
40421         
40422         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40423         // round if off by 1 pixel, otherwise use ceil
40424         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40425         colSpan = Math.min( colSpan, this.cols );
40426         
40427         // normally this should be '1' as we dont' currently allow multi width columns..
40428         
40429         var colGroup = this._getColGroup( colSpan );
40430         // get the minimum Y value from the columns
40431         var minimumY = Math.min.apply( Math, colGroup );
40432         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40433         
40434         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40435          
40436         // position the brick
40437         var position = {
40438             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40439             y: this.currentSize.y + minimumY + this.padHeight
40440         };
40441         
40442         Roo.log(position);
40443         // apply setHeight to necessary columns
40444         var setHeight = minimumY + sz.height + this.padHeight;
40445         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40446         
40447         var setSpan = this.cols + 1 - colGroup.length;
40448         for ( var i = 0; i < setSpan; i++ ) {
40449           this.colYs[ shortColIndex + i ] = setHeight ;
40450         }
40451       
40452         return position;
40453     },
40454     
40455     /**
40456      * @param {Number} colSpan - number of columns the element spans
40457      * @returns {Array} colGroup
40458      */
40459     _getColGroup : function( colSpan )
40460     {
40461         if ( colSpan < 2 ) {
40462           // if brick spans only one column, use all the column Ys
40463           return this.colYs;
40464         }
40465       
40466         var colGroup = [];
40467         // how many different places could this brick fit horizontally
40468         var groupCount = this.cols + 1 - colSpan;
40469         // for each group potential horizontal position
40470         for ( var i = 0; i < groupCount; i++ ) {
40471           // make an array of colY values for that one group
40472           var groupColYs = this.colYs.slice( i, i + colSpan );
40473           // and get the max value of the array
40474           colGroup[i] = Math.max.apply( Math, groupColYs );
40475         }
40476         return colGroup;
40477     },
40478     /*
40479     _manageStamp : function( stamp )
40480     {
40481         var stampSize =  stamp.getSize();
40482         var offset = stamp.getBox();
40483         // get the columns that this stamp affects
40484         var firstX = this.isOriginLeft ? offset.x : offset.right;
40485         var lastX = firstX + stampSize.width;
40486         var firstCol = Math.floor( firstX / this.columnWidth );
40487         firstCol = Math.max( 0, firstCol );
40488         
40489         var lastCol = Math.floor( lastX / this.columnWidth );
40490         // lastCol should not go over if multiple of columnWidth #425
40491         lastCol -= lastX % this.columnWidth ? 0 : 1;
40492         lastCol = Math.min( this.cols - 1, lastCol );
40493         
40494         // set colYs to bottom of the stamp
40495         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40496             stampSize.height;
40497             
40498         for ( var i = firstCol; i <= lastCol; i++ ) {
40499           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40500         }
40501     },
40502     */
40503     
40504     _getContainerSize : function()
40505     {
40506         this.maxY = Math.max.apply( Math, this.colYs );
40507         var size = {
40508             height: this.maxY
40509         };
40510       
40511         if ( this.isFitWidth ) {
40512             size.width = this._getContainerFitWidth();
40513         }
40514       
40515         return size;
40516     },
40517     
40518     _getContainerFitWidth : function()
40519     {
40520         var unusedCols = 0;
40521         // count unused columns
40522         var i = this.cols;
40523         while ( --i ) {
40524           if ( this.colYs[i] !== 0 ) {
40525             break;
40526           }
40527           unusedCols++;
40528         }
40529         // fit container to columns that have been used
40530         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40531     },
40532     
40533     needsResizeLayout : function()
40534     {
40535         var previousWidth = this.containerWidth;
40536         this.getContainerWidth();
40537         return previousWidth !== this.containerWidth;
40538     }
40539  
40540 });
40541
40542  
40543
40544  /*
40545  * - LGPL
40546  *
40547  * element
40548  * 
40549  */
40550
40551 /**
40552  * @class Roo.bootstrap.MasonryBrick
40553  * @extends Roo.bootstrap.Component
40554  * Bootstrap MasonryBrick class
40555  * 
40556  * @constructor
40557  * Create a new MasonryBrick
40558  * @param {Object} config The config object
40559  */
40560
40561 Roo.bootstrap.MasonryBrick = function(config){
40562     
40563     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40564     
40565     Roo.bootstrap.MasonryBrick.register(this);
40566     
40567     this.addEvents({
40568         // raw events
40569         /**
40570          * @event click
40571          * When a MasonryBrick is clcik
40572          * @param {Roo.bootstrap.MasonryBrick} this
40573          * @param {Roo.EventObject} e
40574          */
40575         "click" : true
40576     });
40577 };
40578
40579 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40580     
40581     /**
40582      * @cfg {String} title
40583      */   
40584     title : '',
40585     /**
40586      * @cfg {String} html
40587      */   
40588     html : '',
40589     /**
40590      * @cfg {String} bgimage
40591      */   
40592     bgimage : '',
40593     /**
40594      * @cfg {String} videourl
40595      */   
40596     videourl : '',
40597     /**
40598      * @cfg {String} cls
40599      */   
40600     cls : '',
40601     /**
40602      * @cfg {String} href
40603      */   
40604     href : '',
40605     /**
40606      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40607      */   
40608     size : 'xs',
40609     
40610     /**
40611      * @cfg {String} placetitle (center|bottom)
40612      */   
40613     placetitle : '',
40614     
40615     /**
40616      * @cfg {Boolean} isFitContainer defalut true
40617      */   
40618     isFitContainer : true, 
40619     
40620     /**
40621      * @cfg {Boolean} preventDefault defalut false
40622      */   
40623     preventDefault : false, 
40624     
40625     /**
40626      * @cfg {Boolean} inverse defalut false
40627      */   
40628     maskInverse : false, 
40629     
40630     getAutoCreate : function()
40631     {
40632         if(!this.isFitContainer){
40633             return this.getSplitAutoCreate();
40634         }
40635         
40636         var cls = 'masonry-brick masonry-brick-full';
40637         
40638         if(this.href.length){
40639             cls += ' masonry-brick-link';
40640         }
40641         
40642         if(this.bgimage.length){
40643             cls += ' masonry-brick-image';
40644         }
40645         
40646         if(this.maskInverse){
40647             cls += ' mask-inverse';
40648         }
40649         
40650         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40651             cls += ' enable-mask';
40652         }
40653         
40654         if(this.size){
40655             cls += ' masonry-' + this.size + '-brick';
40656         }
40657         
40658         if(this.placetitle.length){
40659             
40660             switch (this.placetitle) {
40661                 case 'center' :
40662                     cls += ' masonry-center-title';
40663                     break;
40664                 case 'bottom' :
40665                     cls += ' masonry-bottom-title';
40666                     break;
40667                 default:
40668                     break;
40669             }
40670             
40671         } else {
40672             if(!this.html.length && !this.bgimage.length){
40673                 cls += ' masonry-center-title';
40674             }
40675
40676             if(!this.html.length && this.bgimage.length){
40677                 cls += ' masonry-bottom-title';
40678             }
40679         }
40680         
40681         if(this.cls){
40682             cls += ' ' + this.cls;
40683         }
40684         
40685         var cfg = {
40686             tag: (this.href.length) ? 'a' : 'div',
40687             cls: cls,
40688             cn: [
40689                 {
40690                     tag: 'div',
40691                     cls: 'masonry-brick-mask'
40692                 },
40693                 {
40694                     tag: 'div',
40695                     cls: 'masonry-brick-paragraph',
40696                     cn: []
40697                 }
40698             ]
40699         };
40700         
40701         if(this.href.length){
40702             cfg.href = this.href;
40703         }
40704         
40705         var cn = cfg.cn[1].cn;
40706         
40707         if(this.title.length){
40708             cn.push({
40709                 tag: 'h4',
40710                 cls: 'masonry-brick-title',
40711                 html: this.title
40712             });
40713         }
40714         
40715         if(this.html.length){
40716             cn.push({
40717                 tag: 'p',
40718                 cls: 'masonry-brick-text',
40719                 html: this.html
40720             });
40721         }
40722         
40723         if (!this.title.length && !this.html.length) {
40724             cfg.cn[1].cls += ' hide';
40725         }
40726         
40727         if(this.bgimage.length){
40728             cfg.cn.push({
40729                 tag: 'img',
40730                 cls: 'masonry-brick-image-view',
40731                 src: this.bgimage
40732             });
40733         }
40734         
40735         if(this.videourl.length){
40736             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40737             // youtube support only?
40738             cfg.cn.push({
40739                 tag: 'iframe',
40740                 cls: 'masonry-brick-image-view',
40741                 src: vurl,
40742                 frameborder : 0,
40743                 allowfullscreen : true
40744             });
40745         }
40746         
40747         return cfg;
40748         
40749     },
40750     
40751     getSplitAutoCreate : function()
40752     {
40753         var cls = 'masonry-brick masonry-brick-split';
40754         
40755         if(this.href.length){
40756             cls += ' masonry-brick-link';
40757         }
40758         
40759         if(this.bgimage.length){
40760             cls += ' masonry-brick-image';
40761         }
40762         
40763         if(this.size){
40764             cls += ' masonry-' + this.size + '-brick';
40765         }
40766         
40767         switch (this.placetitle) {
40768             case 'center' :
40769                 cls += ' masonry-center-title';
40770                 break;
40771             case 'bottom' :
40772                 cls += ' masonry-bottom-title';
40773                 break;
40774             default:
40775                 if(!this.bgimage.length){
40776                     cls += ' masonry-center-title';
40777                 }
40778
40779                 if(this.bgimage.length){
40780                     cls += ' masonry-bottom-title';
40781                 }
40782                 break;
40783         }
40784         
40785         if(this.cls){
40786             cls += ' ' + this.cls;
40787         }
40788         
40789         var cfg = {
40790             tag: (this.href.length) ? 'a' : 'div',
40791             cls: cls,
40792             cn: [
40793                 {
40794                     tag: 'div',
40795                     cls: 'masonry-brick-split-head',
40796                     cn: [
40797                         {
40798                             tag: 'div',
40799                             cls: 'masonry-brick-paragraph',
40800                             cn: []
40801                         }
40802                     ]
40803                 },
40804                 {
40805                     tag: 'div',
40806                     cls: 'masonry-brick-split-body',
40807                     cn: []
40808                 }
40809             ]
40810         };
40811         
40812         if(this.href.length){
40813             cfg.href = this.href;
40814         }
40815         
40816         if(this.title.length){
40817             cfg.cn[0].cn[0].cn.push({
40818                 tag: 'h4',
40819                 cls: 'masonry-brick-title',
40820                 html: this.title
40821             });
40822         }
40823         
40824         if(this.html.length){
40825             cfg.cn[1].cn.push({
40826                 tag: 'p',
40827                 cls: 'masonry-brick-text',
40828                 html: this.html
40829             });
40830         }
40831
40832         if(this.bgimage.length){
40833             cfg.cn[0].cn.push({
40834                 tag: 'img',
40835                 cls: 'masonry-brick-image-view',
40836                 src: this.bgimage
40837             });
40838         }
40839         
40840         if(this.videourl.length){
40841             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40842             // youtube support only?
40843             cfg.cn[0].cn.cn.push({
40844                 tag: 'iframe',
40845                 cls: 'masonry-brick-image-view',
40846                 src: vurl,
40847                 frameborder : 0,
40848                 allowfullscreen : true
40849             });
40850         }
40851         
40852         return cfg;
40853     },
40854     
40855     initEvents: function() 
40856     {
40857         switch (this.size) {
40858             case 'xs' :
40859                 this.x = 1;
40860                 this.y = 1;
40861                 break;
40862             case 'sm' :
40863                 this.x = 2;
40864                 this.y = 2;
40865                 break;
40866             case 'md' :
40867             case 'md-left' :
40868             case 'md-right' :
40869                 this.x = 3;
40870                 this.y = 3;
40871                 break;
40872             case 'tall' :
40873                 this.x = 2;
40874                 this.y = 3;
40875                 break;
40876             case 'wide' :
40877                 this.x = 3;
40878                 this.y = 2;
40879                 break;
40880             case 'wide-thin' :
40881                 this.x = 3;
40882                 this.y = 1;
40883                 break;
40884                         
40885             default :
40886                 break;
40887         }
40888         
40889         if(Roo.isTouch){
40890             this.el.on('touchstart', this.onTouchStart, this);
40891             this.el.on('touchmove', this.onTouchMove, this);
40892             this.el.on('touchend', this.onTouchEnd, this);
40893             this.el.on('contextmenu', this.onContextMenu, this);
40894         } else {
40895             this.el.on('mouseenter'  ,this.enter, this);
40896             this.el.on('mouseleave', this.leave, this);
40897             this.el.on('click', this.onClick, this);
40898         }
40899         
40900         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40901             this.parent().bricks.push(this);   
40902         }
40903         
40904     },
40905     
40906     onClick: function(e, el)
40907     {
40908         var time = this.endTimer - this.startTimer;
40909         // Roo.log(e.preventDefault());
40910         if(Roo.isTouch){
40911             if(time > 1000){
40912                 e.preventDefault();
40913                 return;
40914             }
40915         }
40916         
40917         if(!this.preventDefault){
40918             return;
40919         }
40920         
40921         e.preventDefault();
40922         
40923         if (this.activeClass != '') {
40924             this.selectBrick();
40925         }
40926         
40927         this.fireEvent('click', this, e);
40928     },
40929     
40930     enter: function(e, el)
40931     {
40932         e.preventDefault();
40933         
40934         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40935             return;
40936         }
40937         
40938         if(this.bgimage.length && this.html.length){
40939             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40940         }
40941     },
40942     
40943     leave: function(e, el)
40944     {
40945         e.preventDefault();
40946         
40947         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40948             return;
40949         }
40950         
40951         if(this.bgimage.length && this.html.length){
40952             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40953         }
40954     },
40955     
40956     onTouchStart: function(e, el)
40957     {
40958 //        e.preventDefault();
40959         
40960         this.touchmoved = false;
40961         
40962         if(!this.isFitContainer){
40963             return;
40964         }
40965         
40966         if(!this.bgimage.length || !this.html.length){
40967             return;
40968         }
40969         
40970         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40971         
40972         this.timer = new Date().getTime();
40973         
40974     },
40975     
40976     onTouchMove: function(e, el)
40977     {
40978         this.touchmoved = true;
40979     },
40980     
40981     onContextMenu : function(e,el)
40982     {
40983         e.preventDefault();
40984         e.stopPropagation();
40985         return false;
40986     },
40987     
40988     onTouchEnd: function(e, el)
40989     {
40990 //        e.preventDefault();
40991         
40992         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40993         
40994             this.leave(e,el);
40995             
40996             return;
40997         }
40998         
40999         if(!this.bgimage.length || !this.html.length){
41000             
41001             if(this.href.length){
41002                 window.location.href = this.href;
41003             }
41004             
41005             return;
41006         }
41007         
41008         if(!this.isFitContainer){
41009             return;
41010         }
41011         
41012         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41013         
41014         window.location.href = this.href;
41015     },
41016     
41017     //selection on single brick only
41018     selectBrick : function() {
41019         
41020         if (!this.parentId) {
41021             return;
41022         }
41023         
41024         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41025         var index = m.selectedBrick.indexOf(this.id);
41026         
41027         if ( index > -1) {
41028             m.selectedBrick.splice(index,1);
41029             this.el.removeClass(this.activeClass);
41030             return;
41031         }
41032         
41033         for(var i = 0; i < m.selectedBrick.length; i++) {
41034             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41035             b.el.removeClass(b.activeClass);
41036         }
41037         
41038         m.selectedBrick = [];
41039         
41040         m.selectedBrick.push(this.id);
41041         this.el.addClass(this.activeClass);
41042         return;
41043     },
41044     
41045     isSelected : function(){
41046         return this.el.hasClass(this.activeClass);
41047         
41048     }
41049 });
41050
41051 Roo.apply(Roo.bootstrap.MasonryBrick, {
41052     
41053     //groups: {},
41054     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41055      /**
41056     * register a Masonry Brick
41057     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41058     */
41059     
41060     register : function(brick)
41061     {
41062         //this.groups[brick.id] = brick;
41063         this.groups.add(brick.id, brick);
41064     },
41065     /**
41066     * fetch a  masonry brick based on the masonry brick ID
41067     * @param {string} the masonry brick to add
41068     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41069     */
41070     
41071     get: function(brick_id) 
41072     {
41073         // if (typeof(this.groups[brick_id]) == 'undefined') {
41074         //     return false;
41075         // }
41076         // return this.groups[brick_id] ;
41077         
41078         if(this.groups.key(brick_id)) {
41079             return this.groups.key(brick_id);
41080         }
41081         
41082         return false;
41083     }
41084     
41085     
41086     
41087 });
41088
41089  /*
41090  * - LGPL
41091  *
41092  * element
41093  * 
41094  */
41095
41096 /**
41097  * @class Roo.bootstrap.Brick
41098  * @extends Roo.bootstrap.Component
41099  * Bootstrap Brick class
41100  * 
41101  * @constructor
41102  * Create a new Brick
41103  * @param {Object} config The config object
41104  */
41105
41106 Roo.bootstrap.Brick = function(config){
41107     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41108     
41109     this.addEvents({
41110         // raw events
41111         /**
41112          * @event click
41113          * When a Brick is click
41114          * @param {Roo.bootstrap.Brick} this
41115          * @param {Roo.EventObject} e
41116          */
41117         "click" : true
41118     });
41119 };
41120
41121 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41122     
41123     /**
41124      * @cfg {String} title
41125      */   
41126     title : '',
41127     /**
41128      * @cfg {String} html
41129      */   
41130     html : '',
41131     /**
41132      * @cfg {String} bgimage
41133      */   
41134     bgimage : '',
41135     /**
41136      * @cfg {String} cls
41137      */   
41138     cls : '',
41139     /**
41140      * @cfg {String} href
41141      */   
41142     href : '',
41143     /**
41144      * @cfg {String} video
41145      */   
41146     video : '',
41147     /**
41148      * @cfg {Boolean} square
41149      */   
41150     square : true,
41151     
41152     getAutoCreate : function()
41153     {
41154         var cls = 'roo-brick';
41155         
41156         if(this.href.length){
41157             cls += ' roo-brick-link';
41158         }
41159         
41160         if(this.bgimage.length){
41161             cls += ' roo-brick-image';
41162         }
41163         
41164         if(!this.html.length && !this.bgimage.length){
41165             cls += ' roo-brick-center-title';
41166         }
41167         
41168         if(!this.html.length && this.bgimage.length){
41169             cls += ' roo-brick-bottom-title';
41170         }
41171         
41172         if(this.cls){
41173             cls += ' ' + this.cls;
41174         }
41175         
41176         var cfg = {
41177             tag: (this.href.length) ? 'a' : 'div',
41178             cls: cls,
41179             cn: [
41180                 {
41181                     tag: 'div',
41182                     cls: 'roo-brick-paragraph',
41183                     cn: []
41184                 }
41185             ]
41186         };
41187         
41188         if(this.href.length){
41189             cfg.href = this.href;
41190         }
41191         
41192         var cn = cfg.cn[0].cn;
41193         
41194         if(this.title.length){
41195             cn.push({
41196                 tag: 'h4',
41197                 cls: 'roo-brick-title',
41198                 html: this.title
41199             });
41200         }
41201         
41202         if(this.html.length){
41203             cn.push({
41204                 tag: 'p',
41205                 cls: 'roo-brick-text',
41206                 html: this.html
41207             });
41208         } else {
41209             cn.cls += ' hide';
41210         }
41211         
41212         if(this.bgimage.length){
41213             cfg.cn.push({
41214                 tag: 'img',
41215                 cls: 'roo-brick-image-view',
41216                 src: this.bgimage
41217             });
41218         }
41219         
41220         return cfg;
41221     },
41222     
41223     initEvents: function() 
41224     {
41225         if(this.title.length || this.html.length){
41226             this.el.on('mouseenter'  ,this.enter, this);
41227             this.el.on('mouseleave', this.leave, this);
41228         }
41229         
41230         Roo.EventManager.onWindowResize(this.resize, this); 
41231         
41232         if(this.bgimage.length){
41233             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41234             this.imageEl.on('load', this.onImageLoad, this);
41235             return;
41236         }
41237         
41238         this.resize();
41239     },
41240     
41241     onImageLoad : function()
41242     {
41243         this.resize();
41244     },
41245     
41246     resize : function()
41247     {
41248         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41249         
41250         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41251         
41252         if(this.bgimage.length){
41253             var image = this.el.select('.roo-brick-image-view', true).first();
41254             
41255             image.setWidth(paragraph.getWidth());
41256             
41257             if(this.square){
41258                 image.setHeight(paragraph.getWidth());
41259             }
41260             
41261             this.el.setHeight(image.getHeight());
41262             paragraph.setHeight(image.getHeight());
41263             
41264         }
41265         
41266     },
41267     
41268     enter: function(e, el)
41269     {
41270         e.preventDefault();
41271         
41272         if(this.bgimage.length){
41273             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41274             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41275         }
41276     },
41277     
41278     leave: function(e, el)
41279     {
41280         e.preventDefault();
41281         
41282         if(this.bgimage.length){
41283             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41284             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41285         }
41286     }
41287     
41288 });
41289
41290  
41291
41292  /*
41293  * - LGPL
41294  *
41295  * Number field 
41296  */
41297
41298 /**
41299  * @class Roo.bootstrap.form.NumberField
41300  * @extends Roo.bootstrap.form.Input
41301  * Bootstrap NumberField class
41302  * 
41303  * 
41304  * 
41305  * 
41306  * @constructor
41307  * Create a new NumberField
41308  * @param {Object} config The config object
41309  */
41310
41311 Roo.bootstrap.form.NumberField = function(config){
41312     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41313 };
41314
41315 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41316     
41317     /**
41318      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41319      */
41320     allowDecimals : true,
41321     /**
41322      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41323      */
41324     decimalSeparator : ".",
41325     /**
41326      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41327      */
41328     decimalPrecision : 2,
41329     /**
41330      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41331      */
41332     allowNegative : true,
41333     
41334     /**
41335      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41336      */
41337     allowZero: true,
41338     /**
41339      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41340      */
41341     minValue : Number.NEGATIVE_INFINITY,
41342     /**
41343      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41344      */
41345     maxValue : Number.MAX_VALUE,
41346     /**
41347      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41348      */
41349     minText : "The minimum value for this field is {0}",
41350     /**
41351      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41352      */
41353     maxText : "The maximum value for this field is {0}",
41354     /**
41355      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41356      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41357      */
41358     nanText : "{0} is not a valid number",
41359     /**
41360      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41361      */
41362     thousandsDelimiter : false,
41363     /**
41364      * @cfg {String} valueAlign alignment of value
41365      */
41366     valueAlign : "left",
41367
41368     getAutoCreate : function()
41369     {
41370         var hiddenInput = {
41371             tag: 'input',
41372             type: 'hidden',
41373             id: Roo.id(),
41374             cls: 'hidden-number-input'
41375         };
41376         
41377         if (this.name) {
41378             hiddenInput.name = this.name;
41379         }
41380         
41381         this.name = '';
41382         
41383         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41384         
41385         this.name = hiddenInput.name;
41386         
41387         if(cfg.cn.length > 0) {
41388             cfg.cn.push(hiddenInput);
41389         }
41390         
41391         return cfg;
41392     },
41393
41394     // private
41395     initEvents : function()
41396     {   
41397         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41398         
41399         var allowed = "0123456789";
41400         
41401         if(this.allowDecimals){
41402             allowed += this.decimalSeparator;
41403         }
41404         
41405         if(this.allowNegative){
41406             allowed += "-";
41407         }
41408         
41409         if(this.thousandsDelimiter) {
41410             allowed += ",";
41411         }
41412         
41413         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41414         
41415         var keyPress = function(e){
41416             
41417             var k = e.getKey();
41418             
41419             var c = e.getCharCode();
41420             
41421             if(
41422                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41423                     allowed.indexOf(String.fromCharCode(c)) === -1
41424             ){
41425                 e.stopEvent();
41426                 return;
41427             }
41428             
41429             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41430                 return;
41431             }
41432             
41433             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41434                 e.stopEvent();
41435             }
41436         };
41437         
41438         this.el.on("keypress", keyPress, this);
41439     },
41440     
41441     validateValue : function(value)
41442     {
41443         
41444         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41445             return false;
41446         }
41447         
41448         var num = this.parseValue(value);
41449         
41450         if(isNaN(num)){
41451             this.markInvalid(String.format(this.nanText, value));
41452             return false;
41453         }
41454         
41455         if(num < this.minValue){
41456             this.markInvalid(String.format(this.minText, this.minValue));
41457             return false;
41458         }
41459         
41460         if(num > this.maxValue){
41461             this.markInvalid(String.format(this.maxText, this.maxValue));
41462             return false;
41463         }
41464         
41465         return true;
41466     },
41467
41468     getValue : function()
41469     {
41470         var v = this.hiddenEl().getValue();
41471         
41472         return this.fixPrecision(this.parseValue(v));
41473     },
41474
41475     parseValue : function(value)
41476     {
41477         if(this.thousandsDelimiter) {
41478             value += "";
41479             r = new RegExp(",", "g");
41480             value = value.replace(r, "");
41481         }
41482         
41483         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41484         return isNaN(value) ? '' : value;
41485     },
41486
41487     fixPrecision : function(value)
41488     {
41489         if(this.thousandsDelimiter) {
41490             value += "";
41491             r = new RegExp(",", "g");
41492             value = value.replace(r, "");
41493         }
41494         
41495         var nan = isNaN(value);
41496         
41497         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41498             return nan ? '' : value;
41499         }
41500         return parseFloat(value).toFixed(this.decimalPrecision);
41501     },
41502
41503     setValue : function(v)
41504     {
41505         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41506         
41507         this.value = v;
41508         
41509         if(this.rendered){
41510             
41511             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41512             
41513             this.inputEl().dom.value = (v == '') ? '' :
41514                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41515             
41516             if(!this.allowZero && v === '0') {
41517                 this.hiddenEl().dom.value = '';
41518                 this.inputEl().dom.value = '';
41519             }
41520             
41521             this.validate();
41522         }
41523     },
41524
41525     decimalPrecisionFcn : function(v)
41526     {
41527         return Math.floor(v);
41528     },
41529
41530     beforeBlur : function()
41531     {
41532         var v = this.parseValue(this.getRawValue());
41533         
41534         if(v || v === 0 || v === ''){
41535             this.setValue(v);
41536         }
41537     },
41538     
41539     hiddenEl : function()
41540     {
41541         return this.el.select('input.hidden-number-input',true).first();
41542     }
41543     
41544 });
41545
41546  
41547
41548 /*
41549 * Licence: LGPL
41550 */
41551
41552 /**
41553  * @class Roo.bootstrap.DocumentSlider
41554  * @extends Roo.bootstrap.Component
41555  * Bootstrap DocumentSlider class
41556  * 
41557  * @constructor
41558  * Create a new DocumentViewer
41559  * @param {Object} config The config object
41560  */
41561
41562 Roo.bootstrap.DocumentSlider = function(config){
41563     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41564     
41565     this.files = [];
41566     
41567     this.addEvents({
41568         /**
41569          * @event initial
41570          * Fire after initEvent
41571          * @param {Roo.bootstrap.DocumentSlider} this
41572          */
41573         "initial" : true,
41574         /**
41575          * @event update
41576          * Fire after update
41577          * @param {Roo.bootstrap.DocumentSlider} this
41578          */
41579         "update" : true,
41580         /**
41581          * @event click
41582          * Fire after click
41583          * @param {Roo.bootstrap.DocumentSlider} this
41584          */
41585         "click" : true
41586     });
41587 };
41588
41589 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41590     
41591     files : false,
41592     
41593     indicator : 0,
41594     
41595     getAutoCreate : function()
41596     {
41597         var cfg = {
41598             tag : 'div',
41599             cls : 'roo-document-slider',
41600             cn : [
41601                 {
41602                     tag : 'div',
41603                     cls : 'roo-document-slider-header',
41604                     cn : [
41605                         {
41606                             tag : 'div',
41607                             cls : 'roo-document-slider-header-title'
41608                         }
41609                     ]
41610                 },
41611                 {
41612                     tag : 'div',
41613                     cls : 'roo-document-slider-body',
41614                     cn : [
41615                         {
41616                             tag : 'div',
41617                             cls : 'roo-document-slider-prev',
41618                             cn : [
41619                                 {
41620                                     tag : 'i',
41621                                     cls : 'fa fa-chevron-left'
41622                                 }
41623                             ]
41624                         },
41625                         {
41626                             tag : 'div',
41627                             cls : 'roo-document-slider-thumb',
41628                             cn : [
41629                                 {
41630                                     tag : 'img',
41631                                     cls : 'roo-document-slider-image'
41632                                 }
41633                             ]
41634                         },
41635                         {
41636                             tag : 'div',
41637                             cls : 'roo-document-slider-next',
41638                             cn : [
41639                                 {
41640                                     tag : 'i',
41641                                     cls : 'fa fa-chevron-right'
41642                                 }
41643                             ]
41644                         }
41645                     ]
41646                 }
41647             ]
41648         };
41649         
41650         return cfg;
41651     },
41652     
41653     initEvents : function()
41654     {
41655         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41656         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41657         
41658         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41659         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41660         
41661         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41662         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41663         
41664         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41665         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41666         
41667         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41668         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41669         
41670         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41671         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41672         
41673         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41674         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41675         
41676         this.thumbEl.on('click', this.onClick, this);
41677         
41678         this.prevIndicator.on('click', this.prev, this);
41679         
41680         this.nextIndicator.on('click', this.next, this);
41681         
41682     },
41683     
41684     initial : function()
41685     {
41686         if(this.files.length){
41687             this.indicator = 1;
41688             this.update()
41689         }
41690         
41691         this.fireEvent('initial', this);
41692     },
41693     
41694     update : function()
41695     {
41696         this.imageEl.attr('src', this.files[this.indicator - 1]);
41697         
41698         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41699         
41700         this.prevIndicator.show();
41701         
41702         if(this.indicator == 1){
41703             this.prevIndicator.hide();
41704         }
41705         
41706         this.nextIndicator.show();
41707         
41708         if(this.indicator == this.files.length){
41709             this.nextIndicator.hide();
41710         }
41711         
41712         this.thumbEl.scrollTo('top');
41713         
41714         this.fireEvent('update', this);
41715     },
41716     
41717     onClick : function(e)
41718     {
41719         e.preventDefault();
41720         
41721         this.fireEvent('click', this);
41722     },
41723     
41724     prev : function(e)
41725     {
41726         e.preventDefault();
41727         
41728         this.indicator = Math.max(1, this.indicator - 1);
41729         
41730         this.update();
41731     },
41732     
41733     next : function(e)
41734     {
41735         e.preventDefault();
41736         
41737         this.indicator = Math.min(this.files.length, this.indicator + 1);
41738         
41739         this.update();
41740     }
41741 });
41742 /*
41743  * - LGPL
41744  *
41745  * RadioSet
41746  *
41747  *
41748  */
41749
41750 /**
41751  * @class Roo.bootstrap.form.RadioSet
41752  * @extends Roo.bootstrap.form.Input
41753  * @children Roo.bootstrap.form.Radio
41754  * Bootstrap RadioSet class
41755  * @cfg {String} indicatorpos (left|right) default left
41756  * @cfg {Boolean} inline (true|false) inline the element (default true)
41757  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41758  * @constructor
41759  * Create a new RadioSet
41760  * @param {Object} config The config object
41761  */
41762
41763 Roo.bootstrap.form.RadioSet = function(config){
41764     
41765     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41766     
41767     this.radioes = [];
41768     
41769     Roo.bootstrap.form.RadioSet.register(this);
41770     
41771     this.addEvents({
41772         /**
41773         * @event check
41774         * Fires when the element is checked or unchecked.
41775         * @param {Roo.bootstrap.form.RadioSet} this This radio
41776         * @param {Roo.bootstrap.form.Radio} item The checked item
41777         */
41778        check : true,
41779        /**
41780         * @event click
41781         * Fires when the element is click.
41782         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41783         * @param {Roo.bootstrap.form.Radio} item The checked item
41784         * @param {Roo.EventObject} e The event object
41785         */
41786        click : true
41787     });
41788     
41789 };
41790
41791 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41792
41793     radioes : false,
41794     
41795     inline : true,
41796     
41797     weight : '',
41798     
41799     indicatorpos : 'left',
41800     
41801     getAutoCreate : function()
41802     {
41803         var label = {
41804             tag : 'label',
41805             cls : 'roo-radio-set-label',
41806             cn : [
41807                 {
41808                     tag : 'span',
41809                     html : this.fieldLabel
41810                 }
41811             ]
41812         };
41813         if (Roo.bootstrap.version == 3) {
41814             
41815             
41816             if(this.indicatorpos == 'left'){
41817                 label.cn.unshift({
41818                     tag : 'i',
41819                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41820                     tooltip : 'This field is required'
41821                 });
41822             } else {
41823                 label.cn.push({
41824                     tag : 'i',
41825                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41826                     tooltip : 'This field is required'
41827                 });
41828             }
41829         }
41830         var items = {
41831             tag : 'div',
41832             cls : 'roo-radio-set-items'
41833         };
41834         
41835         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41836         
41837         if (align === 'left' && this.fieldLabel.length) {
41838             
41839             items = {
41840                 cls : "roo-radio-set-right", 
41841                 cn: [
41842                     items
41843                 ]
41844             };
41845             
41846             if(this.labelWidth > 12){
41847                 label.style = "width: " + this.labelWidth + 'px';
41848             }
41849             
41850             if(this.labelWidth < 13 && this.labelmd == 0){
41851                 this.labelmd = this.labelWidth;
41852             }
41853             
41854             if(this.labellg > 0){
41855                 label.cls += ' col-lg-' + this.labellg;
41856                 items.cls += ' col-lg-' + (12 - this.labellg);
41857             }
41858             
41859             if(this.labelmd > 0){
41860                 label.cls += ' col-md-' + this.labelmd;
41861                 items.cls += ' col-md-' + (12 - this.labelmd);
41862             }
41863             
41864             if(this.labelsm > 0){
41865                 label.cls += ' col-sm-' + this.labelsm;
41866                 items.cls += ' col-sm-' + (12 - this.labelsm);
41867             }
41868             
41869             if(this.labelxs > 0){
41870                 label.cls += ' col-xs-' + this.labelxs;
41871                 items.cls += ' col-xs-' + (12 - this.labelxs);
41872             }
41873         }
41874         
41875         var cfg = {
41876             tag : 'div',
41877             cls : 'roo-radio-set',
41878             cn : [
41879                 {
41880                     tag : 'input',
41881                     cls : 'roo-radio-set-input',
41882                     type : 'hidden',
41883                     name : this.name,
41884                     value : this.value ? this.value :  ''
41885                 },
41886                 label,
41887                 items
41888             ]
41889         };
41890         
41891         if(this.weight.length){
41892             cfg.cls += ' roo-radio-' + this.weight;
41893         }
41894         
41895         if(this.inline) {
41896             cfg.cls += ' roo-radio-set-inline';
41897         }
41898         
41899         var settings=this;
41900         ['xs','sm','md','lg'].map(function(size){
41901             if (settings[size]) {
41902                 cfg.cls += ' col-' + size + '-' + settings[size];
41903             }
41904         });
41905         
41906         return cfg;
41907         
41908     },
41909
41910     initEvents : function()
41911     {
41912         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41913         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41914         
41915         if(!this.fieldLabel.length){
41916             this.labelEl.hide();
41917         }
41918         
41919         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41920         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41921         
41922         this.indicator = this.indicatorEl();
41923         
41924         if(this.indicator){
41925             this.indicator.addClass('invisible');
41926         }
41927         
41928         this.originalValue = this.getValue();
41929         
41930     },
41931     
41932     inputEl: function ()
41933     {
41934         return this.el.select('.roo-radio-set-input', true).first();
41935     },
41936     
41937     getChildContainer : function()
41938     {
41939         return this.itemsEl;
41940     },
41941     
41942     register : function(item)
41943     {
41944         this.radioes.push(item);
41945         
41946     },
41947     
41948     validate : function()
41949     {   
41950         if(this.getVisibilityEl().hasClass('hidden')){
41951             return true;
41952         }
41953         
41954         var valid = false;
41955         
41956         Roo.each(this.radioes, function(i){
41957             if(!i.checked){
41958                 return;
41959             }
41960             
41961             valid = true;
41962             return false;
41963         });
41964         
41965         if(this.allowBlank) {
41966             return true;
41967         }
41968         
41969         if(this.disabled || valid){
41970             this.markValid();
41971             return true;
41972         }
41973         
41974         this.markInvalid();
41975         return false;
41976         
41977     },
41978     
41979     markValid : function()
41980     {
41981         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41982             this.indicatorEl().removeClass('visible');
41983             this.indicatorEl().addClass('invisible');
41984         }
41985         
41986         
41987         if (Roo.bootstrap.version == 3) {
41988             this.el.removeClass([this.invalidClass, this.validClass]);
41989             this.el.addClass(this.validClass);
41990         } else {
41991             this.el.removeClass(['is-invalid','is-valid']);
41992             this.el.addClass(['is-valid']);
41993         }
41994         this.fireEvent('valid', this);
41995     },
41996     
41997     markInvalid : function(msg)
41998     {
41999         if(this.allowBlank || this.disabled){
42000             return;
42001         }
42002         
42003         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42004             this.indicatorEl().removeClass('invisible');
42005             this.indicatorEl().addClass('visible');
42006         }
42007         if (Roo.bootstrap.version == 3) {
42008             this.el.removeClass([this.invalidClass, this.validClass]);
42009             this.el.addClass(this.invalidClass);
42010         } else {
42011             this.el.removeClass(['is-invalid','is-valid']);
42012             this.el.addClass(['is-invalid']);
42013         }
42014         
42015         this.fireEvent('invalid', this, msg);
42016         
42017     },
42018     
42019     setValue : function(v, suppressEvent)
42020     {   
42021         if(this.value === v){
42022             return;
42023         }
42024         
42025         this.value = v;
42026         
42027         if(this.rendered){
42028             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42029         }
42030         
42031         Roo.each(this.radioes, function(i){
42032             i.checked = false;
42033             i.el.removeClass('checked');
42034         });
42035         
42036         Roo.each(this.radioes, function(i){
42037             
42038             if(i.value === v || i.value.toString() === v.toString()){
42039                 i.checked = true;
42040                 i.el.addClass('checked');
42041                 
42042                 if(suppressEvent !== true){
42043                     this.fireEvent('check', this, i);
42044                 }
42045                 
42046                 return false;
42047             }
42048             
42049         }, this);
42050         
42051         this.validate();
42052     },
42053     
42054     clearInvalid : function(){
42055         
42056         if(!this.el || this.preventMark){
42057             return;
42058         }
42059         
42060         this.el.removeClass([this.invalidClass]);
42061         
42062         this.fireEvent('valid', this);
42063     }
42064     
42065 });
42066
42067 Roo.apply(Roo.bootstrap.form.RadioSet, {
42068     
42069     groups: {},
42070     
42071     register : function(set)
42072     {
42073         this.groups[set.name] = set;
42074     },
42075     
42076     get: function(name) 
42077     {
42078         if (typeof(this.groups[name]) == 'undefined') {
42079             return false;
42080         }
42081         
42082         return this.groups[name] ;
42083     }
42084     
42085 });
42086 /*
42087  * Based on:
42088  * Ext JS Library 1.1.1
42089  * Copyright(c) 2006-2007, Ext JS, LLC.
42090  *
42091  * Originally Released Under LGPL - original licence link has changed is not relivant.
42092  *
42093  * Fork - LGPL
42094  * <script type="text/javascript">
42095  */
42096
42097
42098 /**
42099  * @class Roo.bootstrap.SplitBar
42100  * @extends Roo.util.Observable
42101  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42102  * <br><br>
42103  * Usage:
42104  * <pre><code>
42105 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42106                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42107 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42108 split.minSize = 100;
42109 split.maxSize = 600;
42110 split.animate = true;
42111 split.on('moved', splitterMoved);
42112 </code></pre>
42113  * @constructor
42114  * Create a new SplitBar
42115  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42116  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42117  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42118  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42119                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42120                         position of the SplitBar).
42121  */
42122 Roo.bootstrap.SplitBar = function(cfg){
42123     
42124     /** @private */
42125     
42126     //{
42127     //  dragElement : elm
42128     //  resizingElement: el,
42129         // optional..
42130     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42131     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42132         // existingProxy ???
42133     //}
42134     
42135     this.el = Roo.get(cfg.dragElement, true);
42136     this.el.dom.unselectable = "on";
42137     /** @private */
42138     this.resizingEl = Roo.get(cfg.resizingElement, true);
42139
42140     /**
42141      * @private
42142      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42143      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42144      * @type Number
42145      */
42146     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42147     
42148     /**
42149      * The minimum size of the resizing element. (Defaults to 0)
42150      * @type Number
42151      */
42152     this.minSize = 0;
42153     
42154     /**
42155      * The maximum size of the resizing element. (Defaults to 2000)
42156      * @type Number
42157      */
42158     this.maxSize = 2000;
42159     
42160     /**
42161      * Whether to animate the transition to the new size
42162      * @type Boolean
42163      */
42164     this.animate = false;
42165     
42166     /**
42167      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42168      * @type Boolean
42169      */
42170     this.useShim = false;
42171     
42172     /** @private */
42173     this.shim = null;
42174     
42175     if(!cfg.existingProxy){
42176         /** @private */
42177         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42178     }else{
42179         this.proxy = Roo.get(cfg.existingProxy).dom;
42180     }
42181     /** @private */
42182     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42183     
42184     /** @private */
42185     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42186     
42187     /** @private */
42188     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42189     
42190     /** @private */
42191     this.dragSpecs = {};
42192     
42193     /**
42194      * @private The adapter to use to positon and resize elements
42195      */
42196     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42197     this.adapter.init(this);
42198     
42199     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42200         /** @private */
42201         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42202         this.el.addClass("roo-splitbar-h");
42203     }else{
42204         /** @private */
42205         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42206         this.el.addClass("roo-splitbar-v");
42207     }
42208     
42209     this.addEvents({
42210         /**
42211          * @event resize
42212          * Fires when the splitter is moved (alias for {@link #event-moved})
42213          * @param {Roo.bootstrap.SplitBar} this
42214          * @param {Number} newSize the new width or height
42215          */
42216         "resize" : true,
42217         /**
42218          * @event moved
42219          * Fires when the splitter is moved
42220          * @param {Roo.bootstrap.SplitBar} this
42221          * @param {Number} newSize the new width or height
42222          */
42223         "moved" : true,
42224         /**
42225          * @event beforeresize
42226          * Fires before the splitter is dragged
42227          * @param {Roo.bootstrap.SplitBar} this
42228          */
42229         "beforeresize" : true,
42230
42231         "beforeapply" : true
42232     });
42233
42234     Roo.util.Observable.call(this);
42235 };
42236
42237 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42238     onStartProxyDrag : function(x, y){
42239         this.fireEvent("beforeresize", this);
42240         if(!this.overlay){
42241             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42242             o.unselectable();
42243             o.enableDisplayMode("block");
42244             // all splitbars share the same overlay
42245             Roo.bootstrap.SplitBar.prototype.overlay = o;
42246         }
42247         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42248         this.overlay.show();
42249         Roo.get(this.proxy).setDisplayed("block");
42250         var size = this.adapter.getElementSize(this);
42251         this.activeMinSize = this.getMinimumSize();;
42252         this.activeMaxSize = this.getMaximumSize();;
42253         var c1 = size - this.activeMinSize;
42254         var c2 = Math.max(this.activeMaxSize - size, 0);
42255         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42256             this.dd.resetConstraints();
42257             this.dd.setXConstraint(
42258                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42259                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42260             );
42261             this.dd.setYConstraint(0, 0);
42262         }else{
42263             this.dd.resetConstraints();
42264             this.dd.setXConstraint(0, 0);
42265             this.dd.setYConstraint(
42266                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42267                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42268             );
42269          }
42270         this.dragSpecs.startSize = size;
42271         this.dragSpecs.startPoint = [x, y];
42272         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42273     },
42274     
42275     /** 
42276      * @private Called after the drag operation by the DDProxy
42277      */
42278     onEndProxyDrag : function(e){
42279         Roo.get(this.proxy).setDisplayed(false);
42280         var endPoint = Roo.lib.Event.getXY(e);
42281         if(this.overlay){
42282             this.overlay.hide();
42283         }
42284         var newSize;
42285         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42286             newSize = this.dragSpecs.startSize + 
42287                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42288                     endPoint[0] - this.dragSpecs.startPoint[0] :
42289                     this.dragSpecs.startPoint[0] - endPoint[0]
42290                 );
42291         }else{
42292             newSize = this.dragSpecs.startSize + 
42293                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42294                     endPoint[1] - this.dragSpecs.startPoint[1] :
42295                     this.dragSpecs.startPoint[1] - endPoint[1]
42296                 );
42297         }
42298         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42299         if(newSize != this.dragSpecs.startSize){
42300             if(this.fireEvent('beforeapply', this, newSize) !== false){
42301                 this.adapter.setElementSize(this, newSize);
42302                 this.fireEvent("moved", this, newSize);
42303                 this.fireEvent("resize", this, newSize);
42304             }
42305         }
42306     },
42307     
42308     /**
42309      * Get the adapter this SplitBar uses
42310      * @return The adapter object
42311      */
42312     getAdapter : function(){
42313         return this.adapter;
42314     },
42315     
42316     /**
42317      * Set the adapter this SplitBar uses
42318      * @param {Object} adapter A SplitBar adapter object
42319      */
42320     setAdapter : function(adapter){
42321         this.adapter = adapter;
42322         this.adapter.init(this);
42323     },
42324     
42325     /**
42326      * Gets the minimum size for the resizing element
42327      * @return {Number} The minimum size
42328      */
42329     getMinimumSize : function(){
42330         return this.minSize;
42331     },
42332     
42333     /**
42334      * Sets the minimum size for the resizing element
42335      * @param {Number} minSize The minimum size
42336      */
42337     setMinimumSize : function(minSize){
42338         this.minSize = minSize;
42339     },
42340     
42341     /**
42342      * Gets the maximum size for the resizing element
42343      * @return {Number} The maximum size
42344      */
42345     getMaximumSize : function(){
42346         return this.maxSize;
42347     },
42348     
42349     /**
42350      * Sets the maximum size for the resizing element
42351      * @param {Number} maxSize The maximum size
42352      */
42353     setMaximumSize : function(maxSize){
42354         this.maxSize = maxSize;
42355     },
42356     
42357     /**
42358      * Sets the initialize size for the resizing element
42359      * @param {Number} size The initial size
42360      */
42361     setCurrentSize : function(size){
42362         var oldAnimate = this.animate;
42363         this.animate = false;
42364         this.adapter.setElementSize(this, size);
42365         this.animate = oldAnimate;
42366     },
42367     
42368     /**
42369      * Destroy this splitbar. 
42370      * @param {Boolean} removeEl True to remove the element
42371      */
42372     destroy : function(removeEl){
42373         if(this.shim){
42374             this.shim.remove();
42375         }
42376         this.dd.unreg();
42377         this.proxy.parentNode.removeChild(this.proxy);
42378         if(removeEl){
42379             this.el.remove();
42380         }
42381     }
42382 });
42383
42384 /**
42385  * @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.
42386  */
42387 Roo.bootstrap.SplitBar.createProxy = function(dir){
42388     var proxy = new Roo.Element(document.createElement("div"));
42389     proxy.unselectable();
42390     var cls = 'roo-splitbar-proxy';
42391     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42392     document.body.appendChild(proxy.dom);
42393     return proxy.dom;
42394 };
42395
42396 /** 
42397  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42398  * Default Adapter. It assumes the splitter and resizing element are not positioned
42399  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42400  */
42401 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42402 };
42403
42404 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42405     // do nothing for now
42406     init : function(s){
42407     
42408     },
42409     /**
42410      * Called before drag operations to get the current size of the resizing element. 
42411      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42412      */
42413      getElementSize : function(s){
42414         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42415             return s.resizingEl.getWidth();
42416         }else{
42417             return s.resizingEl.getHeight();
42418         }
42419     },
42420     
42421     /**
42422      * Called after drag operations to set the size of the resizing element.
42423      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42424      * @param {Number} newSize The new size to set
42425      * @param {Function} onComplete A function to be invoked when resizing is complete
42426      */
42427     setElementSize : function(s, newSize, onComplete){
42428         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42429             if(!s.animate){
42430                 s.resizingEl.setWidth(newSize);
42431                 if(onComplete){
42432                     onComplete(s, newSize);
42433                 }
42434             }else{
42435                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42436             }
42437         }else{
42438             
42439             if(!s.animate){
42440                 s.resizingEl.setHeight(newSize);
42441                 if(onComplete){
42442                     onComplete(s, newSize);
42443                 }
42444             }else{
42445                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42446             }
42447         }
42448     }
42449 };
42450
42451 /** 
42452  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42453  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42454  * Adapter that  moves the splitter element to align with the resized sizing element. 
42455  * Used with an absolute positioned SplitBar.
42456  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42457  * document.body, make sure you assign an id to the body element.
42458  */
42459 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42460     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42461     this.container = Roo.get(container);
42462 };
42463
42464 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42465     init : function(s){
42466         this.basic.init(s);
42467     },
42468     
42469     getElementSize : function(s){
42470         return this.basic.getElementSize(s);
42471     },
42472     
42473     setElementSize : function(s, newSize, onComplete){
42474         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42475     },
42476     
42477     moveSplitter : function(s){
42478         var yes = Roo.bootstrap.SplitBar;
42479         switch(s.placement){
42480             case yes.LEFT:
42481                 s.el.setX(s.resizingEl.getRight());
42482                 break;
42483             case yes.RIGHT:
42484                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42485                 break;
42486             case yes.TOP:
42487                 s.el.setY(s.resizingEl.getBottom());
42488                 break;
42489             case yes.BOTTOM:
42490                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42491                 break;
42492         }
42493     }
42494 };
42495
42496 /**
42497  * Orientation constant - Create a vertical SplitBar
42498  * @static
42499  * @type Number
42500  */
42501 Roo.bootstrap.SplitBar.VERTICAL = 1;
42502
42503 /**
42504  * Orientation constant - Create a horizontal SplitBar
42505  * @static
42506  * @type Number
42507  */
42508 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42509
42510 /**
42511  * Placement constant - The resizing element is to the left of the splitter element
42512  * @static
42513  * @type Number
42514  */
42515 Roo.bootstrap.SplitBar.LEFT = 1;
42516
42517 /**
42518  * Placement constant - The resizing element is to the right of the splitter element
42519  * @static
42520  * @type Number
42521  */
42522 Roo.bootstrap.SplitBar.RIGHT = 2;
42523
42524 /**
42525  * Placement constant - The resizing element is positioned above the splitter element
42526  * @static
42527  * @type Number
42528  */
42529 Roo.bootstrap.SplitBar.TOP = 3;
42530
42531 /**
42532  * Placement constant - The resizing element is positioned under splitter element
42533  * @static
42534  * @type Number
42535  */
42536 Roo.bootstrap.SplitBar.BOTTOM = 4;
42537 /*
42538  * Based on:
42539  * Ext JS Library 1.1.1
42540  * Copyright(c) 2006-2007, Ext JS, LLC.
42541  *
42542  * Originally Released Under LGPL - original licence link has changed is not relivant.
42543  *
42544  * Fork - LGPL
42545  * <script type="text/javascript">
42546  */
42547
42548 /**
42549  * @class Roo.bootstrap.layout.Manager
42550  * @extends Roo.bootstrap.Component
42551  * @abstract
42552  * Base class for layout managers.
42553  */
42554 Roo.bootstrap.layout.Manager = function(config)
42555 {
42556     this.monitorWindowResize = true; // do this before we apply configuration.
42557     
42558     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42559
42560
42561
42562
42563
42564     /** false to disable window resize monitoring @type Boolean */
42565     
42566     this.regions = {};
42567     this.addEvents({
42568         /**
42569          * @event layout
42570          * Fires when a layout is performed.
42571          * @param {Roo.LayoutManager} this
42572          */
42573         "layout" : true,
42574         /**
42575          * @event regionresized
42576          * Fires when the user resizes a region.
42577          * @param {Roo.LayoutRegion} region The resized region
42578          * @param {Number} newSize The new size (width for east/west, height for north/south)
42579          */
42580         "regionresized" : true,
42581         /**
42582          * @event regioncollapsed
42583          * Fires when a region is collapsed.
42584          * @param {Roo.LayoutRegion} region The collapsed region
42585          */
42586         "regioncollapsed" : true,
42587         /**
42588          * @event regionexpanded
42589          * Fires when a region is expanded.
42590          * @param {Roo.LayoutRegion} region The expanded region
42591          */
42592         "regionexpanded" : true
42593     });
42594     this.updating = false;
42595
42596     if (config.el) {
42597         this.el = Roo.get(config.el);
42598         this.initEvents();
42599     }
42600
42601 };
42602
42603 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42604
42605
42606     regions : null,
42607
42608     monitorWindowResize : true,
42609
42610
42611     updating : false,
42612
42613
42614     onRender : function(ct, position)
42615     {
42616         if(!this.el){
42617             this.el = Roo.get(ct);
42618             this.initEvents();
42619         }
42620         //this.fireEvent('render',this);
42621     },
42622
42623
42624     initEvents: function()
42625     {
42626
42627
42628         // ie scrollbar fix
42629         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42630             document.body.scroll = "no";
42631         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42632             this.el.position('relative');
42633         }
42634         this.id = this.el.id;
42635         this.el.addClass("roo-layout-container");
42636         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42637         if(this.el.dom != document.body ) {
42638             this.el.on('resize', this.layout,this);
42639             this.el.on('show', this.layout,this);
42640         }
42641
42642     },
42643
42644     /**
42645      * Returns true if this layout is currently being updated
42646      * @return {Boolean}
42647      */
42648     isUpdating : function(){
42649         return this.updating;
42650     },
42651
42652     /**
42653      * Suspend the LayoutManager from doing auto-layouts while
42654      * making multiple add or remove calls
42655      */
42656     beginUpdate : function(){
42657         this.updating = true;
42658     },
42659
42660     /**
42661      * Restore auto-layouts and optionally disable the manager from performing a layout
42662      * @param {Boolean} noLayout true to disable a layout update
42663      */
42664     endUpdate : function(noLayout){
42665         this.updating = false;
42666         if(!noLayout){
42667             this.layout();
42668         }
42669     },
42670
42671     layout: function(){
42672         // abstract...
42673     },
42674
42675     onRegionResized : function(region, newSize){
42676         this.fireEvent("regionresized", region, newSize);
42677         this.layout();
42678     },
42679
42680     onRegionCollapsed : function(region){
42681         this.fireEvent("regioncollapsed", region);
42682     },
42683
42684     onRegionExpanded : function(region){
42685         this.fireEvent("regionexpanded", region);
42686     },
42687
42688     /**
42689      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42690      * performs box-model adjustments.
42691      * @return {Object} The size as an object {width: (the width), height: (the height)}
42692      */
42693     getViewSize : function()
42694     {
42695         var size;
42696         if(this.el.dom != document.body){
42697             size = this.el.getSize();
42698         }else{
42699             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42700         }
42701         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42702         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42703         return size;
42704     },
42705
42706     /**
42707      * Returns the Element this layout is bound to.
42708      * @return {Roo.Element}
42709      */
42710     getEl : function(){
42711         return this.el;
42712     },
42713
42714     /**
42715      * Returns the specified region.
42716      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42717      * @return {Roo.LayoutRegion}
42718      */
42719     getRegion : function(target){
42720         return this.regions[target.toLowerCase()];
42721     },
42722
42723     onWindowResize : function(){
42724         if(this.monitorWindowResize){
42725             this.layout();
42726         }
42727     }
42728 });
42729 /*
42730  * Based on:
42731  * Ext JS Library 1.1.1
42732  * Copyright(c) 2006-2007, Ext JS, LLC.
42733  *
42734  * Originally Released Under LGPL - original licence link has changed is not relivant.
42735  *
42736  * Fork - LGPL
42737  * <script type="text/javascript">
42738  */
42739 /**
42740  * @class Roo.bootstrap.layout.Border
42741  * @extends Roo.bootstrap.layout.Manager
42742  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42743  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42744  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42745  * please see: examples/bootstrap/nested.html<br><br>
42746  
42747 <b>The container the layout is rendered into can be either the body element or any other element.
42748 If it is not the body element, the container needs to either be an absolute positioned element,
42749 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42750 the container size if it is not the body element.</b>
42751
42752 * @constructor
42753 * Create a new Border
42754 * @param {Object} config Configuration options
42755  */
42756 Roo.bootstrap.layout.Border = function(config){
42757     config = config || {};
42758     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42759     
42760     
42761     
42762     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42763         if(config[region]){
42764             config[region].region = region;
42765             this.addRegion(config[region]);
42766         }
42767     },this);
42768     
42769 };
42770
42771 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42772
42773 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42774     
42775         /**
42776          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42777          */
42778         /**
42779          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42780          */
42781         /**
42782          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42783          */
42784         /**
42785          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42786          */
42787         /**
42788          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42789          */
42790         
42791         
42792         
42793         
42794     parent : false, // this might point to a 'nest' or a ???
42795     
42796     /**
42797      * Creates and adds a new region if it doesn't already exist.
42798      * @param {String} target The target region key (north, south, east, west or center).
42799      * @param {Object} config The regions config object
42800      * @return {BorderLayoutRegion} The new region
42801      */
42802     addRegion : function(config)
42803     {
42804         if(!this.regions[config.region]){
42805             var r = this.factory(config);
42806             this.bindRegion(r);
42807         }
42808         return this.regions[config.region];
42809     },
42810
42811     // private (kinda)
42812     bindRegion : function(r){
42813         this.regions[r.config.region] = r;
42814         
42815         r.on("visibilitychange",    this.layout, this);
42816         r.on("paneladded",          this.layout, this);
42817         r.on("panelremoved",        this.layout, this);
42818         r.on("invalidated",         this.layout, this);
42819         r.on("resized",             this.onRegionResized, this);
42820         r.on("collapsed",           this.onRegionCollapsed, this);
42821         r.on("expanded",            this.onRegionExpanded, this);
42822     },
42823
42824     /**
42825      * Performs a layout update.
42826      */
42827     layout : function()
42828     {
42829         if(this.updating) {
42830             return;
42831         }
42832         
42833         // render all the rebions if they have not been done alreayd?
42834         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42835             if(this.regions[region] && !this.regions[region].bodyEl){
42836                 this.regions[region].onRender(this.el)
42837             }
42838         },this);
42839         
42840         var size = this.getViewSize();
42841         var w = size.width;
42842         var h = size.height;
42843         var centerW = w;
42844         var centerH = h;
42845         var centerY = 0;
42846         var centerX = 0;
42847         //var x = 0, y = 0;
42848
42849         var rs = this.regions;
42850         var north = rs["north"];
42851         var south = rs["south"]; 
42852         var west = rs["west"];
42853         var east = rs["east"];
42854         var center = rs["center"];
42855         //if(this.hideOnLayout){ // not supported anymore
42856             //c.el.setStyle("display", "none");
42857         //}
42858         if(north && north.isVisible()){
42859             var b = north.getBox();
42860             var m = north.getMargins();
42861             b.width = w - (m.left+m.right);
42862             b.x = m.left;
42863             b.y = m.top;
42864             centerY = b.height + b.y + m.bottom;
42865             centerH -= centerY;
42866             north.updateBox(this.safeBox(b));
42867         }
42868         if(south && south.isVisible()){
42869             var b = south.getBox();
42870             var m = south.getMargins();
42871             b.width = w - (m.left+m.right);
42872             b.x = m.left;
42873             var totalHeight = (b.height + m.top + m.bottom);
42874             b.y = h - totalHeight + m.top;
42875             centerH -= totalHeight;
42876             south.updateBox(this.safeBox(b));
42877         }
42878         if(west && west.isVisible()){
42879             var b = west.getBox();
42880             var m = west.getMargins();
42881             b.height = centerH - (m.top+m.bottom);
42882             b.x = m.left;
42883             b.y = centerY + m.top;
42884             var totalWidth = (b.width + m.left + m.right);
42885             centerX += totalWidth;
42886             centerW -= totalWidth;
42887             west.updateBox(this.safeBox(b));
42888         }
42889         if(east && east.isVisible()){
42890             var b = east.getBox();
42891             var m = east.getMargins();
42892             b.height = centerH - (m.top+m.bottom);
42893             var totalWidth = (b.width + m.left + m.right);
42894             b.x = w - totalWidth + m.left;
42895             b.y = centerY + m.top;
42896             centerW -= totalWidth;
42897             east.updateBox(this.safeBox(b));
42898         }
42899         if(center){
42900             var m = center.getMargins();
42901             var centerBox = {
42902                 x: centerX + m.left,
42903                 y: centerY + m.top,
42904                 width: centerW - (m.left+m.right),
42905                 height: centerH - (m.top+m.bottom)
42906             };
42907             //if(this.hideOnLayout){
42908                 //center.el.setStyle("display", "block");
42909             //}
42910             center.updateBox(this.safeBox(centerBox));
42911         }
42912         this.el.repaint();
42913         this.fireEvent("layout", this);
42914     },
42915
42916     // private
42917     safeBox : function(box){
42918         box.width = Math.max(0, box.width);
42919         box.height = Math.max(0, box.height);
42920         return box;
42921     },
42922
42923     /**
42924      * Adds a ContentPanel (or subclass) to this layout.
42925      * @param {String} target The target region key (north, south, east, west or center).
42926      * @param {Roo.ContentPanel} panel The panel to add
42927      * @return {Roo.ContentPanel} The added panel
42928      */
42929     add : function(target, panel){
42930          
42931         target = target.toLowerCase();
42932         return this.regions[target].add(panel);
42933     },
42934
42935     /**
42936      * Remove a ContentPanel (or subclass) to this layout.
42937      * @param {String} target The target region key (north, south, east, west or center).
42938      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42939      * @return {Roo.ContentPanel} The removed panel
42940      */
42941     remove : function(target, panel){
42942         target = target.toLowerCase();
42943         return this.regions[target].remove(panel);
42944     },
42945
42946     /**
42947      * Searches all regions for a panel with the specified id
42948      * @param {String} panelId
42949      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42950      */
42951     findPanel : function(panelId){
42952         var rs = this.regions;
42953         for(var target in rs){
42954             if(typeof rs[target] != "function"){
42955                 var p = rs[target].getPanel(panelId);
42956                 if(p){
42957                     return p;
42958                 }
42959             }
42960         }
42961         return null;
42962     },
42963
42964     /**
42965      * Searches all regions for a panel with the specified id and activates (shows) it.
42966      * @param {String/ContentPanel} panelId The panels id or the panel itself
42967      * @return {Roo.ContentPanel} The shown panel or null
42968      */
42969     showPanel : function(panelId) {
42970       var rs = this.regions;
42971       for(var target in rs){
42972          var r = rs[target];
42973          if(typeof r != "function"){
42974             if(r.hasPanel(panelId)){
42975                return r.showPanel(panelId);
42976             }
42977          }
42978       }
42979       return null;
42980    },
42981
42982    /**
42983      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42984      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42985      */
42986    /*
42987     restoreState : function(provider){
42988         if(!provider){
42989             provider = Roo.state.Manager;
42990         }
42991         var sm = new Roo.LayoutStateManager();
42992         sm.init(this, provider);
42993     },
42994 */
42995  
42996  
42997     /**
42998      * Adds a xtype elements to the layout.
42999      * <pre><code>
43000
43001 layout.addxtype({
43002        xtype : 'ContentPanel',
43003        region: 'west',
43004        items: [ .... ]
43005    }
43006 );
43007
43008 layout.addxtype({
43009         xtype : 'NestedLayoutPanel',
43010         region: 'west',
43011         layout: {
43012            center: { },
43013            west: { }   
43014         },
43015         items : [ ... list of content panels or nested layout panels.. ]
43016    }
43017 );
43018 </code></pre>
43019      * @param {Object} cfg Xtype definition of item to add.
43020      */
43021     addxtype : function(cfg)
43022     {
43023         // basically accepts a pannel...
43024         // can accept a layout region..!?!?
43025         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43026         
43027         
43028         // theory?  children can only be panels??
43029         
43030         //if (!cfg.xtype.match(/Panel$/)) {
43031         //    return false;
43032         //}
43033         var ret = false;
43034         
43035         if (typeof(cfg.region) == 'undefined') {
43036             Roo.log("Failed to add Panel, region was not set");
43037             Roo.log(cfg);
43038             return false;
43039         }
43040         var region = cfg.region;
43041         delete cfg.region;
43042         
43043           
43044         var xitems = [];
43045         if (cfg.items) {
43046             xitems = cfg.items;
43047             delete cfg.items;
43048         }
43049         var nb = false;
43050         
43051         if ( region == 'center') {
43052             Roo.log("Center: " + cfg.title);
43053         }
43054         
43055         
43056         switch(cfg.xtype) 
43057         {
43058             case 'Content':  // ContentPanel (el, cfg)
43059             case 'Scroll':  // ContentPanel (el, cfg)
43060             case 'View': 
43061                 cfg.autoCreate = cfg.autoCreate || true;
43062                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43063                 //} else {
43064                 //    var el = this.el.createChild();
43065                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43066                 //}
43067                 
43068                 this.add(region, ret);
43069                 break;
43070             
43071             /*
43072             case 'TreePanel': // our new panel!
43073                 cfg.el = this.el.createChild();
43074                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43075                 this.add(region, ret);
43076                 break;
43077             */
43078             
43079             case 'Nest': 
43080                 // create a new Layout (which is  a Border Layout...
43081                 
43082                 var clayout = cfg.layout;
43083                 clayout.el  = this.el.createChild();
43084                 clayout.items   = clayout.items  || [];
43085                 
43086                 delete cfg.layout;
43087                 
43088                 // replace this exitems with the clayout ones..
43089                 xitems = clayout.items;
43090                  
43091                 // force background off if it's in center...
43092                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43093                     cfg.background = false;
43094                 }
43095                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43096                 
43097                 
43098                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43099                 //console.log('adding nested layout panel '  + cfg.toSource());
43100                 this.add(region, ret);
43101                 nb = {}; /// find first...
43102                 break;
43103             
43104             case 'Grid':
43105                 
43106                 // needs grid and region
43107                 
43108                 //var el = this.getRegion(region).el.createChild();
43109                 /*
43110                  *var el = this.el.createChild();
43111                 // create the grid first...
43112                 cfg.grid.container = el;
43113                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43114                 */
43115                 
43116                 if (region == 'center' && this.active ) {
43117                     cfg.background = false;
43118                 }
43119                 
43120                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43121                 
43122                 this.add(region, ret);
43123                 /*
43124                 if (cfg.background) {
43125                     // render grid on panel activation (if panel background)
43126                     ret.on('activate', function(gp) {
43127                         if (!gp.grid.rendered) {
43128                     //        gp.grid.render(el);
43129                         }
43130                     });
43131                 } else {
43132                   //  cfg.grid.render(el);
43133                 }
43134                 */
43135                 break;
43136            
43137            
43138             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43139                 // it was the old xcomponent building that caused this before.
43140                 // espeically if border is the top element in the tree.
43141                 ret = this;
43142                 break; 
43143                 
43144                     
43145                 
43146                 
43147                 
43148             default:
43149                 /*
43150                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43151                     
43152                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43153                     this.add(region, ret);
43154                 } else {
43155                 */
43156                     Roo.log(cfg);
43157                     throw "Can not add '" + cfg.xtype + "' to Border";
43158                     return null;
43159              
43160                                 
43161              
43162         }
43163         this.beginUpdate();
43164         // add children..
43165         var region = '';
43166         var abn = {};
43167         Roo.each(xitems, function(i)  {
43168             region = nb && i.region ? i.region : false;
43169             
43170             var add = ret.addxtype(i);
43171            
43172             if (region) {
43173                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43174                 if (!i.background) {
43175                     abn[region] = nb[region] ;
43176                 }
43177             }
43178             
43179         });
43180         this.endUpdate();
43181
43182         // make the last non-background panel active..
43183         //if (nb) { Roo.log(abn); }
43184         if (nb) {
43185             
43186             for(var r in abn) {
43187                 region = this.getRegion(r);
43188                 if (region) {
43189                     // tried using nb[r], but it does not work..
43190                      
43191                     region.showPanel(abn[r]);
43192                    
43193                 }
43194             }
43195         }
43196         return ret;
43197         
43198     },
43199     
43200     
43201 // private
43202     factory : function(cfg)
43203     {
43204         
43205         var validRegions = Roo.bootstrap.layout.Border.regions;
43206
43207         var target = cfg.region;
43208         cfg.mgr = this;
43209         
43210         var r = Roo.bootstrap.layout;
43211         Roo.log(target);
43212         switch(target){
43213             case "north":
43214                 return new r.North(cfg);
43215             case "south":
43216                 return new r.South(cfg);
43217             case "east":
43218                 return new r.East(cfg);
43219             case "west":
43220                 return new r.West(cfg);
43221             case "center":
43222                 return new r.Center(cfg);
43223         }
43224         throw 'Layout region "'+target+'" not supported.';
43225     }
43226     
43227     
43228 });
43229  /*
43230  * Based on:
43231  * Ext JS Library 1.1.1
43232  * Copyright(c) 2006-2007, Ext JS, LLC.
43233  *
43234  * Originally Released Under LGPL - original licence link has changed is not relivant.
43235  *
43236  * Fork - LGPL
43237  * <script type="text/javascript">
43238  */
43239  
43240 /**
43241  * @class Roo.bootstrap.layout.Basic
43242  * @extends Roo.util.Observable
43243  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43244  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43245  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43246  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43247  * @cfg {string}   region  the region that it inhabits..
43248  * @cfg {bool}   skipConfig skip config?
43249  * 
43250
43251  */
43252 Roo.bootstrap.layout.Basic = function(config){
43253     
43254     this.mgr = config.mgr;
43255     
43256     this.position = config.region;
43257     
43258     var skipConfig = config.skipConfig;
43259     
43260     this.events = {
43261         /**
43262          * @scope Roo.BasicLayoutRegion
43263          */
43264         
43265         /**
43266          * @event beforeremove
43267          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43268          * @param {Roo.LayoutRegion} this
43269          * @param {Roo.ContentPanel} panel The panel
43270          * @param {Object} e The cancel event object
43271          */
43272         "beforeremove" : true,
43273         /**
43274          * @event invalidated
43275          * Fires when the layout for this region is changed.
43276          * @param {Roo.LayoutRegion} this
43277          */
43278         "invalidated" : true,
43279         /**
43280          * @event visibilitychange
43281          * Fires when this region is shown or hidden 
43282          * @param {Roo.LayoutRegion} this
43283          * @param {Boolean} visibility true or false
43284          */
43285         "visibilitychange" : true,
43286         /**
43287          * @event paneladded
43288          * Fires when a panel is added. 
43289          * @param {Roo.LayoutRegion} this
43290          * @param {Roo.ContentPanel} panel The panel
43291          */
43292         "paneladded" : true,
43293         /**
43294          * @event panelremoved
43295          * Fires when a panel is removed. 
43296          * @param {Roo.LayoutRegion} this
43297          * @param {Roo.ContentPanel} panel The panel
43298          */
43299         "panelremoved" : true,
43300         /**
43301          * @event beforecollapse
43302          * Fires when this region before collapse.
43303          * @param {Roo.LayoutRegion} this
43304          */
43305         "beforecollapse" : true,
43306         /**
43307          * @event collapsed
43308          * Fires when this region is collapsed.
43309          * @param {Roo.LayoutRegion} this
43310          */
43311         "collapsed" : true,
43312         /**
43313          * @event expanded
43314          * Fires when this region is expanded.
43315          * @param {Roo.LayoutRegion} this
43316          */
43317         "expanded" : true,
43318         /**
43319          * @event slideshow
43320          * Fires when this region is slid into view.
43321          * @param {Roo.LayoutRegion} this
43322          */
43323         "slideshow" : true,
43324         /**
43325          * @event slidehide
43326          * Fires when this region slides out of view. 
43327          * @param {Roo.LayoutRegion} this
43328          */
43329         "slidehide" : true,
43330         /**
43331          * @event panelactivated
43332          * Fires when a panel is activated. 
43333          * @param {Roo.LayoutRegion} this
43334          * @param {Roo.ContentPanel} panel The activated panel
43335          */
43336         "panelactivated" : true,
43337         /**
43338          * @event resized
43339          * Fires when the user resizes this region. 
43340          * @param {Roo.LayoutRegion} this
43341          * @param {Number} newSize The new size (width for east/west, height for north/south)
43342          */
43343         "resized" : true
43344     };
43345     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43346     this.panels = new Roo.util.MixedCollection();
43347     this.panels.getKey = this.getPanelId.createDelegate(this);
43348     this.box = null;
43349     this.activePanel = null;
43350     // ensure listeners are added...
43351     
43352     if (config.listeners || config.events) {
43353         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43354             listeners : config.listeners || {},
43355             events : config.events || {}
43356         });
43357     }
43358     
43359     if(skipConfig !== true){
43360         this.applyConfig(config);
43361     }
43362 };
43363
43364 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43365 {
43366     getPanelId : function(p){
43367         return p.getId();
43368     },
43369     
43370     applyConfig : function(config){
43371         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43372         this.config = config;
43373         
43374     },
43375     
43376     /**
43377      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43378      * the width, for horizontal (north, south) the height.
43379      * @param {Number} newSize The new width or height
43380      */
43381     resizeTo : function(newSize){
43382         var el = this.el ? this.el :
43383                  (this.activePanel ? this.activePanel.getEl() : null);
43384         if(el){
43385             switch(this.position){
43386                 case "east":
43387                 case "west":
43388                     el.setWidth(newSize);
43389                     this.fireEvent("resized", this, newSize);
43390                 break;
43391                 case "north":
43392                 case "south":
43393                     el.setHeight(newSize);
43394                     this.fireEvent("resized", this, newSize);
43395                 break;                
43396             }
43397         }
43398     },
43399     
43400     getBox : function(){
43401         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43402     },
43403     
43404     getMargins : function(){
43405         return this.margins;
43406     },
43407     
43408     updateBox : function(box){
43409         this.box = box;
43410         var el = this.activePanel.getEl();
43411         el.dom.style.left = box.x + "px";
43412         el.dom.style.top = box.y + "px";
43413         this.activePanel.setSize(box.width, box.height);
43414     },
43415     
43416     /**
43417      * Returns the container element for this region.
43418      * @return {Roo.Element}
43419      */
43420     getEl : function(){
43421         return this.activePanel;
43422     },
43423     
43424     /**
43425      * Returns true if this region is currently visible.
43426      * @return {Boolean}
43427      */
43428     isVisible : function(){
43429         return this.activePanel ? true : false;
43430     },
43431     
43432     setActivePanel : function(panel){
43433         panel = this.getPanel(panel);
43434         if(this.activePanel && this.activePanel != panel){
43435             this.activePanel.setActiveState(false);
43436             this.activePanel.getEl().setLeftTop(-10000,-10000);
43437         }
43438         this.activePanel = panel;
43439         panel.setActiveState(true);
43440         if(this.box){
43441             panel.setSize(this.box.width, this.box.height);
43442         }
43443         this.fireEvent("panelactivated", this, panel);
43444         this.fireEvent("invalidated");
43445     },
43446     
43447     /**
43448      * Show the specified panel.
43449      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43450      * @return {Roo.ContentPanel} The shown panel or null
43451      */
43452     showPanel : function(panel){
43453         panel = this.getPanel(panel);
43454         if(panel){
43455             this.setActivePanel(panel);
43456         }
43457         return panel;
43458     },
43459     
43460     /**
43461      * Get the active panel for this region.
43462      * @return {Roo.ContentPanel} The active panel or null
43463      */
43464     getActivePanel : function(){
43465         return this.activePanel;
43466     },
43467     
43468     /**
43469      * Add the passed ContentPanel(s)
43470      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43471      * @return {Roo.ContentPanel} The panel added (if only one was added)
43472      */
43473     add : function(panel){
43474         if(arguments.length > 1){
43475             for(var i = 0, len = arguments.length; i < len; i++) {
43476                 this.add(arguments[i]);
43477             }
43478             return null;
43479         }
43480         if(this.hasPanel(panel)){
43481             this.showPanel(panel);
43482             return panel;
43483         }
43484         var el = panel.getEl();
43485         if(el.dom.parentNode != this.mgr.el.dom){
43486             this.mgr.el.dom.appendChild(el.dom);
43487         }
43488         if(panel.setRegion){
43489             panel.setRegion(this);
43490         }
43491         this.panels.add(panel);
43492         el.setStyle("position", "absolute");
43493         if(!panel.background){
43494             this.setActivePanel(panel);
43495             if(this.config.initialSize && this.panels.getCount()==1){
43496                 this.resizeTo(this.config.initialSize);
43497             }
43498         }
43499         this.fireEvent("paneladded", this, panel);
43500         return panel;
43501     },
43502     
43503     /**
43504      * Returns true if the panel is in this region.
43505      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43506      * @return {Boolean}
43507      */
43508     hasPanel : function(panel){
43509         if(typeof panel == "object"){ // must be panel obj
43510             panel = panel.getId();
43511         }
43512         return this.getPanel(panel) ? true : false;
43513     },
43514     
43515     /**
43516      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43517      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43518      * @param {Boolean} preservePanel Overrides the config preservePanel option
43519      * @return {Roo.ContentPanel} The panel that was removed
43520      */
43521     remove : function(panel, preservePanel){
43522         panel = this.getPanel(panel);
43523         if(!panel){
43524             return null;
43525         }
43526         var e = {};
43527         this.fireEvent("beforeremove", this, panel, e);
43528         if(e.cancel === true){
43529             return null;
43530         }
43531         var panelId = panel.getId();
43532         this.panels.removeKey(panelId);
43533         return panel;
43534     },
43535     
43536     /**
43537      * Returns the panel specified or null if it's not in this region.
43538      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43539      * @return {Roo.ContentPanel}
43540      */
43541     getPanel : function(id){
43542         if(typeof id == "object"){ // must be panel obj
43543             return id;
43544         }
43545         return this.panels.get(id);
43546     },
43547     
43548     /**
43549      * Returns this regions position (north/south/east/west/center).
43550      * @return {String} 
43551      */
43552     getPosition: function(){
43553         return this.position;    
43554     }
43555 });/*
43556  * Based on:
43557  * Ext JS Library 1.1.1
43558  * Copyright(c) 2006-2007, Ext JS, LLC.
43559  *
43560  * Originally Released Under LGPL - original licence link has changed is not relivant.
43561  *
43562  * Fork - LGPL
43563  * <script type="text/javascript">
43564  */
43565  
43566 /**
43567  * @class Roo.bootstrap.layout.Region
43568  * @extends Roo.bootstrap.layout.Basic
43569  * This class represents a region in a layout manager.
43570  
43571  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43572  * @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})
43573  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43574  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43575  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43576  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43577  * @cfg {String}    title           The title for the region (overrides panel titles)
43578  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43579  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43580  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43581  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43582  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43583  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43584  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43585  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43586  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43587  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43588
43589  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43590  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43591  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43592  * @cfg {Number}    width           For East/West panels
43593  * @cfg {Number}    height          For North/South panels
43594  * @cfg {Boolean}   split           To show the splitter
43595  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43596  * 
43597  * @cfg {string}   cls             Extra CSS classes to add to region
43598  * 
43599  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43600  * @cfg {string}   region  the region that it inhabits..
43601  *
43602
43603  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43604  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43605
43606  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43607  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43608  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43609  */
43610 Roo.bootstrap.layout.Region = function(config)
43611 {
43612     this.applyConfig(config);
43613
43614     var mgr = config.mgr;
43615     var pos = config.region;
43616     config.skipConfig = true;
43617     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43618     
43619     if (mgr.el) {
43620         this.onRender(mgr.el);   
43621     }
43622      
43623     this.visible = true;
43624     this.collapsed = false;
43625     this.unrendered_panels = [];
43626 };
43627
43628 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43629
43630     position: '', // set by wrapper (eg. north/south etc..)
43631     unrendered_panels : null,  // unrendered panels.
43632     
43633     tabPosition : false,
43634     
43635     mgr: false, // points to 'Border'
43636     
43637     
43638     createBody : function(){
43639         /** This region's body element 
43640         * @type Roo.Element */
43641         this.bodyEl = this.el.createChild({
43642                 tag: "div",
43643                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43644         });
43645     },
43646
43647     onRender: function(ctr, pos)
43648     {
43649         var dh = Roo.DomHelper;
43650         /** This region's container element 
43651         * @type Roo.Element */
43652         this.el = dh.append(ctr.dom, {
43653                 tag: "div",
43654                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43655             }, true);
43656         /** This region's title element 
43657         * @type Roo.Element */
43658     
43659         this.titleEl = dh.append(this.el.dom,  {
43660                 tag: "div",
43661                 unselectable: "on",
43662                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43663                 children:[
43664                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43665                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43666                 ]
43667             }, true);
43668         
43669         this.titleEl.enableDisplayMode();
43670         /** This region's title text element 
43671         * @type HTMLElement */
43672         this.titleTextEl = this.titleEl.dom.firstChild;
43673         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43674         /*
43675         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43676         this.closeBtn.enableDisplayMode();
43677         this.closeBtn.on("click", this.closeClicked, this);
43678         this.closeBtn.hide();
43679     */
43680         this.createBody(this.config);
43681         if(this.config.hideWhenEmpty){
43682             this.hide();
43683             this.on("paneladded", this.validateVisibility, this);
43684             this.on("panelremoved", this.validateVisibility, this);
43685         }
43686         if(this.autoScroll){
43687             this.bodyEl.setStyle("overflow", "auto");
43688         }else{
43689             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43690         }
43691         //if(c.titlebar !== false){
43692             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43693                 this.titleEl.hide();
43694             }else{
43695                 this.titleEl.show();
43696                 if(this.config.title){
43697                     this.titleTextEl.innerHTML = this.config.title;
43698                 }
43699             }
43700         //}
43701         if(this.config.collapsed){
43702             this.collapse(true);
43703         }
43704         if(this.config.hidden){
43705             this.hide();
43706         }
43707         
43708         if (this.unrendered_panels && this.unrendered_panels.length) {
43709             for (var i =0;i< this.unrendered_panels.length; i++) {
43710                 this.add(this.unrendered_panels[i]);
43711             }
43712             this.unrendered_panels = null;
43713             
43714         }
43715         
43716     },
43717     
43718     applyConfig : function(c)
43719     {
43720         /*
43721          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43722             var dh = Roo.DomHelper;
43723             if(c.titlebar !== false){
43724                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43725                 this.collapseBtn.on("click", this.collapse, this);
43726                 this.collapseBtn.enableDisplayMode();
43727                 /*
43728                 if(c.showPin === true || this.showPin){
43729                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43730                     this.stickBtn.enableDisplayMode();
43731                     this.stickBtn.on("click", this.expand, this);
43732                     this.stickBtn.hide();
43733                 }
43734                 
43735             }
43736             */
43737             /** This region's collapsed element
43738             * @type Roo.Element */
43739             /*
43740              *
43741             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43742                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43743             ]}, true);
43744             
43745             if(c.floatable !== false){
43746                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43747                this.collapsedEl.on("click", this.collapseClick, this);
43748             }
43749
43750             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43751                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43752                    id: "message", unselectable: "on", style:{"float":"left"}});
43753                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43754              }
43755             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43756             this.expandBtn.on("click", this.expand, this);
43757             
43758         }
43759         
43760         if(this.collapseBtn){
43761             this.collapseBtn.setVisible(c.collapsible == true);
43762         }
43763         
43764         this.cmargins = c.cmargins || this.cmargins ||
43765                          (this.position == "west" || this.position == "east" ?
43766                              {top: 0, left: 2, right:2, bottom: 0} :
43767                              {top: 2, left: 0, right:0, bottom: 2});
43768         */
43769         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43770         
43771         
43772         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43773         
43774         this.autoScroll = c.autoScroll || false;
43775         
43776         
43777        
43778         
43779         this.duration = c.duration || .30;
43780         this.slideDuration = c.slideDuration || .45;
43781         this.config = c;
43782        
43783     },
43784     /**
43785      * Returns true if this region is currently visible.
43786      * @return {Boolean}
43787      */
43788     isVisible : function(){
43789         return this.visible;
43790     },
43791
43792     /**
43793      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43794      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43795      */
43796     //setCollapsedTitle : function(title){
43797     //    title = title || "&#160;";
43798      //   if(this.collapsedTitleTextEl){
43799       //      this.collapsedTitleTextEl.innerHTML = title;
43800        // }
43801     //},
43802
43803     getBox : function(){
43804         var b;
43805       //  if(!this.collapsed){
43806             b = this.el.getBox(false, true);
43807        // }else{
43808           //  b = this.collapsedEl.getBox(false, true);
43809         //}
43810         return b;
43811     },
43812
43813     getMargins : function(){
43814         return this.margins;
43815         //return this.collapsed ? this.cmargins : this.margins;
43816     },
43817 /*
43818     highlight : function(){
43819         this.el.addClass("x-layout-panel-dragover");
43820     },
43821
43822     unhighlight : function(){
43823         this.el.removeClass("x-layout-panel-dragover");
43824     },
43825 */
43826     updateBox : function(box)
43827     {
43828         if (!this.bodyEl) {
43829             return; // not rendered yet..
43830         }
43831         
43832         this.box = box;
43833         if(!this.collapsed){
43834             this.el.dom.style.left = box.x + "px";
43835             this.el.dom.style.top = box.y + "px";
43836             this.updateBody(box.width, box.height);
43837         }else{
43838             this.collapsedEl.dom.style.left = box.x + "px";
43839             this.collapsedEl.dom.style.top = box.y + "px";
43840             this.collapsedEl.setSize(box.width, box.height);
43841         }
43842         if(this.tabs){
43843             this.tabs.autoSizeTabs();
43844         }
43845     },
43846
43847     updateBody : function(w, h)
43848     {
43849         if(w !== null){
43850             this.el.setWidth(w);
43851             w -= this.el.getBorderWidth("rl");
43852             if(this.config.adjustments){
43853                 w += this.config.adjustments[0];
43854             }
43855         }
43856         if(h !== null && h > 0){
43857             this.el.setHeight(h);
43858             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43859             h -= this.el.getBorderWidth("tb");
43860             if(this.config.adjustments){
43861                 h += this.config.adjustments[1];
43862             }
43863             this.bodyEl.setHeight(h);
43864             if(this.tabs){
43865                 h = this.tabs.syncHeight(h);
43866             }
43867         }
43868         if(this.panelSize){
43869             w = w !== null ? w : this.panelSize.width;
43870             h = h !== null ? h : this.panelSize.height;
43871         }
43872         if(this.activePanel){
43873             var el = this.activePanel.getEl();
43874             w = w !== null ? w : el.getWidth();
43875             h = h !== null ? h : el.getHeight();
43876             this.panelSize = {width: w, height: h};
43877             this.activePanel.setSize(w, h);
43878         }
43879         if(Roo.isIE && this.tabs){
43880             this.tabs.el.repaint();
43881         }
43882     },
43883
43884     /**
43885      * Returns the container element for this region.
43886      * @return {Roo.Element}
43887      */
43888     getEl : function(){
43889         return this.el;
43890     },
43891
43892     /**
43893      * Hides this region.
43894      */
43895     hide : function(){
43896         //if(!this.collapsed){
43897             this.el.dom.style.left = "-2000px";
43898             this.el.hide();
43899         //}else{
43900          //   this.collapsedEl.dom.style.left = "-2000px";
43901          //   this.collapsedEl.hide();
43902        // }
43903         this.visible = false;
43904         this.fireEvent("visibilitychange", this, false);
43905     },
43906
43907     /**
43908      * Shows this region if it was previously hidden.
43909      */
43910     show : function(){
43911         //if(!this.collapsed){
43912             this.el.show();
43913         //}else{
43914         //    this.collapsedEl.show();
43915        // }
43916         this.visible = true;
43917         this.fireEvent("visibilitychange", this, true);
43918     },
43919 /*
43920     closeClicked : function(){
43921         if(this.activePanel){
43922             this.remove(this.activePanel);
43923         }
43924     },
43925
43926     collapseClick : function(e){
43927         if(this.isSlid){
43928            e.stopPropagation();
43929            this.slideIn();
43930         }else{
43931            e.stopPropagation();
43932            this.slideOut();
43933         }
43934     },
43935 */
43936     /**
43937      * Collapses this region.
43938      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43939      */
43940     /*
43941     collapse : function(skipAnim, skipCheck = false){
43942         if(this.collapsed) {
43943             return;
43944         }
43945         
43946         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43947             
43948             this.collapsed = true;
43949             if(this.split){
43950                 this.split.el.hide();
43951             }
43952             if(this.config.animate && skipAnim !== true){
43953                 this.fireEvent("invalidated", this);
43954                 this.animateCollapse();
43955             }else{
43956                 this.el.setLocation(-20000,-20000);
43957                 this.el.hide();
43958                 this.collapsedEl.show();
43959                 this.fireEvent("collapsed", this);
43960                 this.fireEvent("invalidated", this);
43961             }
43962         }
43963         
43964     },
43965 */
43966     animateCollapse : function(){
43967         // overridden
43968     },
43969
43970     /**
43971      * Expands this region if it was previously collapsed.
43972      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43973      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43974      */
43975     /*
43976     expand : function(e, skipAnim){
43977         if(e) {
43978             e.stopPropagation();
43979         }
43980         if(!this.collapsed || this.el.hasActiveFx()) {
43981             return;
43982         }
43983         if(this.isSlid){
43984             this.afterSlideIn();
43985             skipAnim = true;
43986         }
43987         this.collapsed = false;
43988         if(this.config.animate && skipAnim !== true){
43989             this.animateExpand();
43990         }else{
43991             this.el.show();
43992             if(this.split){
43993                 this.split.el.show();
43994             }
43995             this.collapsedEl.setLocation(-2000,-2000);
43996             this.collapsedEl.hide();
43997             this.fireEvent("invalidated", this);
43998             this.fireEvent("expanded", this);
43999         }
44000     },
44001 */
44002     animateExpand : function(){
44003         // overridden
44004     },
44005
44006     initTabs : function()
44007     {
44008         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44009         
44010         var ts = new Roo.bootstrap.panel.Tabs({
44011             el: this.bodyEl.dom,
44012             region : this,
44013             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44014             disableTooltips: this.config.disableTabTips,
44015             toolbar : this.config.toolbar
44016         });
44017         
44018         if(this.config.hideTabs){
44019             ts.stripWrap.setDisplayed(false);
44020         }
44021         this.tabs = ts;
44022         ts.resizeTabs = this.config.resizeTabs === true;
44023         ts.minTabWidth = this.config.minTabWidth || 40;
44024         ts.maxTabWidth = this.config.maxTabWidth || 250;
44025         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44026         ts.monitorResize = false;
44027         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44028         ts.bodyEl.addClass('roo-layout-tabs-body');
44029         this.panels.each(this.initPanelAsTab, this);
44030     },
44031
44032     initPanelAsTab : function(panel){
44033         var ti = this.tabs.addTab(
44034             panel.getEl().id,
44035             panel.getTitle(),
44036             null,
44037             this.config.closeOnTab && panel.isClosable(),
44038             panel.tpl
44039         );
44040         if(panel.tabTip !== undefined){
44041             ti.setTooltip(panel.tabTip);
44042         }
44043         ti.on("activate", function(){
44044               this.setActivePanel(panel);
44045         }, this);
44046         
44047         if(this.config.closeOnTab){
44048             ti.on("beforeclose", function(t, e){
44049                 e.cancel = true;
44050                 this.remove(panel);
44051             }, this);
44052         }
44053         
44054         panel.tabItem = ti;
44055         
44056         return ti;
44057     },
44058
44059     updatePanelTitle : function(panel, title)
44060     {
44061         if(this.activePanel == panel){
44062             this.updateTitle(title);
44063         }
44064         if(this.tabs){
44065             var ti = this.tabs.getTab(panel.getEl().id);
44066             ti.setText(title);
44067             if(panel.tabTip !== undefined){
44068                 ti.setTooltip(panel.tabTip);
44069             }
44070         }
44071     },
44072
44073     updateTitle : function(title){
44074         if(this.titleTextEl && !this.config.title){
44075             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44076         }
44077     },
44078
44079     setActivePanel : function(panel)
44080     {
44081         panel = this.getPanel(panel);
44082         if(this.activePanel && this.activePanel != panel){
44083             if(this.activePanel.setActiveState(false) === false){
44084                 return;
44085             }
44086         }
44087         this.activePanel = panel;
44088         panel.setActiveState(true);
44089         if(this.panelSize){
44090             panel.setSize(this.panelSize.width, this.panelSize.height);
44091         }
44092         if(this.closeBtn){
44093             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44094         }
44095         this.updateTitle(panel.getTitle());
44096         if(this.tabs){
44097             this.fireEvent("invalidated", this);
44098         }
44099         this.fireEvent("panelactivated", this, panel);
44100     },
44101
44102     /**
44103      * Shows the specified panel.
44104      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44105      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44106      */
44107     showPanel : function(panel)
44108     {
44109         panel = this.getPanel(panel);
44110         if(panel){
44111             if(this.tabs){
44112                 var tab = this.tabs.getTab(panel.getEl().id);
44113                 if(tab.isHidden()){
44114                     this.tabs.unhideTab(tab.id);
44115                 }
44116                 tab.activate();
44117             }else{
44118                 this.setActivePanel(panel);
44119             }
44120         }
44121         return panel;
44122     },
44123
44124     /**
44125      * Get the active panel for this region.
44126      * @return {Roo.ContentPanel} The active panel or null
44127      */
44128     getActivePanel : function(){
44129         return this.activePanel;
44130     },
44131
44132     validateVisibility : function(){
44133         if(this.panels.getCount() < 1){
44134             this.updateTitle("&#160;");
44135             this.closeBtn.hide();
44136             this.hide();
44137         }else{
44138             if(!this.isVisible()){
44139                 this.show();
44140             }
44141         }
44142     },
44143
44144     /**
44145      * Adds the passed ContentPanel(s) to this region.
44146      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44147      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44148      */
44149     add : function(panel)
44150     {
44151         if(arguments.length > 1){
44152             for(var i = 0, len = arguments.length; i < len; i++) {
44153                 this.add(arguments[i]);
44154             }
44155             return null;
44156         }
44157         
44158         // if we have not been rendered yet, then we can not really do much of this..
44159         if (!this.bodyEl) {
44160             this.unrendered_panels.push(panel);
44161             return panel;
44162         }
44163         
44164         
44165         
44166         
44167         if(this.hasPanel(panel)){
44168             this.showPanel(panel);
44169             return panel;
44170         }
44171         panel.setRegion(this);
44172         this.panels.add(panel);
44173        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44174             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44175             // and hide them... ???
44176             this.bodyEl.dom.appendChild(panel.getEl().dom);
44177             if(panel.background !== true){
44178                 this.setActivePanel(panel);
44179             }
44180             this.fireEvent("paneladded", this, panel);
44181             return panel;
44182         }
44183         */
44184         if(!this.tabs){
44185             this.initTabs();
44186         }else{
44187             this.initPanelAsTab(panel);
44188         }
44189         
44190         
44191         if(panel.background !== true){
44192             this.tabs.activate(panel.getEl().id);
44193         }
44194         this.fireEvent("paneladded", this, panel);
44195         return panel;
44196     },
44197
44198     /**
44199      * Hides the tab for the specified panel.
44200      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44201      */
44202     hidePanel : function(panel){
44203         if(this.tabs && (panel = this.getPanel(panel))){
44204             this.tabs.hideTab(panel.getEl().id);
44205         }
44206     },
44207
44208     /**
44209      * Unhides the tab for a previously hidden panel.
44210      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44211      */
44212     unhidePanel : function(panel){
44213         if(this.tabs && (panel = this.getPanel(panel))){
44214             this.tabs.unhideTab(panel.getEl().id);
44215         }
44216     },
44217
44218     clearPanels : function(){
44219         while(this.panels.getCount() > 0){
44220              this.remove(this.panels.first());
44221         }
44222     },
44223
44224     /**
44225      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44226      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44227      * @param {Boolean} preservePanel Overrides the config preservePanel option
44228      * @return {Roo.ContentPanel} The panel that was removed
44229      */
44230     remove : function(panel, preservePanel)
44231     {
44232         panel = this.getPanel(panel);
44233         if(!panel){
44234             return null;
44235         }
44236         var e = {};
44237         this.fireEvent("beforeremove", this, panel, e);
44238         if(e.cancel === true){
44239             return null;
44240         }
44241         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44242         var panelId = panel.getId();
44243         this.panels.removeKey(panelId);
44244         if(preservePanel){
44245             document.body.appendChild(panel.getEl().dom);
44246         }
44247         if(this.tabs){
44248             this.tabs.removeTab(panel.getEl().id);
44249         }else if (!preservePanel){
44250             this.bodyEl.dom.removeChild(panel.getEl().dom);
44251         }
44252         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44253             var p = this.panels.first();
44254             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44255             tempEl.appendChild(p.getEl().dom);
44256             this.bodyEl.update("");
44257             this.bodyEl.dom.appendChild(p.getEl().dom);
44258             tempEl = null;
44259             this.updateTitle(p.getTitle());
44260             this.tabs = null;
44261             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44262             this.setActivePanel(p);
44263         }
44264         panel.setRegion(null);
44265         if(this.activePanel == panel){
44266             this.activePanel = null;
44267         }
44268         if(this.config.autoDestroy !== false && preservePanel !== true){
44269             try{panel.destroy();}catch(e){}
44270         }
44271         this.fireEvent("panelremoved", this, panel);
44272         return panel;
44273     },
44274
44275     /**
44276      * Returns the TabPanel component used by this region
44277      * @return {Roo.TabPanel}
44278      */
44279     getTabs : function(){
44280         return this.tabs;
44281     },
44282
44283     createTool : function(parentEl, className){
44284         var btn = Roo.DomHelper.append(parentEl, {
44285             tag: "div",
44286             cls: "x-layout-tools-button",
44287             children: [ {
44288                 tag: "div",
44289                 cls: "roo-layout-tools-button-inner " + className,
44290                 html: "&#160;"
44291             }]
44292         }, true);
44293         btn.addClassOnOver("roo-layout-tools-button-over");
44294         return btn;
44295     }
44296 });/*
44297  * Based on:
44298  * Ext JS Library 1.1.1
44299  * Copyright(c) 2006-2007, Ext JS, LLC.
44300  *
44301  * Originally Released Under LGPL - original licence link has changed is not relivant.
44302  *
44303  * Fork - LGPL
44304  * <script type="text/javascript">
44305  */
44306  
44307
44308
44309 /**
44310  * @class Roo.SplitLayoutRegion
44311  * @extends Roo.LayoutRegion
44312  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44313  */
44314 Roo.bootstrap.layout.Split = function(config){
44315     this.cursor = config.cursor;
44316     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44317 };
44318
44319 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44320 {
44321     splitTip : "Drag to resize.",
44322     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44323     useSplitTips : false,
44324
44325     applyConfig : function(config){
44326         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44327     },
44328     
44329     onRender : function(ctr,pos) {
44330         
44331         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44332         if(!this.config.split){
44333             return;
44334         }
44335         if(!this.split){
44336             
44337             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44338                             tag: "div",
44339                             id: this.el.id + "-split",
44340                             cls: "roo-layout-split roo-layout-split-"+this.position,
44341                             html: "&#160;"
44342             });
44343             /** The SplitBar for this region 
44344             * @type Roo.SplitBar */
44345             // does not exist yet...
44346             Roo.log([this.position, this.orientation]);
44347             
44348             this.split = new Roo.bootstrap.SplitBar({
44349                 dragElement : splitEl,
44350                 resizingElement: this.el,
44351                 orientation : this.orientation
44352             });
44353             
44354             this.split.on("moved", this.onSplitMove, this);
44355             this.split.useShim = this.config.useShim === true;
44356             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44357             if(this.useSplitTips){
44358                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44359             }
44360             //if(config.collapsible){
44361             //    this.split.el.on("dblclick", this.collapse,  this);
44362             //}
44363         }
44364         if(typeof this.config.minSize != "undefined"){
44365             this.split.minSize = this.config.minSize;
44366         }
44367         if(typeof this.config.maxSize != "undefined"){
44368             this.split.maxSize = this.config.maxSize;
44369         }
44370         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44371             this.hideSplitter();
44372         }
44373         
44374     },
44375
44376     getHMaxSize : function(){
44377          var cmax = this.config.maxSize || 10000;
44378          var center = this.mgr.getRegion("center");
44379          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44380     },
44381
44382     getVMaxSize : function(){
44383          var cmax = this.config.maxSize || 10000;
44384          var center = this.mgr.getRegion("center");
44385          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44386     },
44387
44388     onSplitMove : function(split, newSize){
44389         this.fireEvent("resized", this, newSize);
44390     },
44391     
44392     /** 
44393      * Returns the {@link Roo.SplitBar} for this region.
44394      * @return {Roo.SplitBar}
44395      */
44396     getSplitBar : function(){
44397         return this.split;
44398     },
44399     
44400     hide : function(){
44401         this.hideSplitter();
44402         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44403     },
44404
44405     hideSplitter : function(){
44406         if(this.split){
44407             this.split.el.setLocation(-2000,-2000);
44408             this.split.el.hide();
44409         }
44410     },
44411
44412     show : function(){
44413         if(this.split){
44414             this.split.el.show();
44415         }
44416         Roo.bootstrap.layout.Split.superclass.show.call(this);
44417     },
44418     
44419     beforeSlide: function(){
44420         if(Roo.isGecko){// firefox overflow auto bug workaround
44421             this.bodyEl.clip();
44422             if(this.tabs) {
44423                 this.tabs.bodyEl.clip();
44424             }
44425             if(this.activePanel){
44426                 this.activePanel.getEl().clip();
44427                 
44428                 if(this.activePanel.beforeSlide){
44429                     this.activePanel.beforeSlide();
44430                 }
44431             }
44432         }
44433     },
44434     
44435     afterSlide : function(){
44436         if(Roo.isGecko){// firefox overflow auto bug workaround
44437             this.bodyEl.unclip();
44438             if(this.tabs) {
44439                 this.tabs.bodyEl.unclip();
44440             }
44441             if(this.activePanel){
44442                 this.activePanel.getEl().unclip();
44443                 if(this.activePanel.afterSlide){
44444                     this.activePanel.afterSlide();
44445                 }
44446             }
44447         }
44448     },
44449
44450     initAutoHide : function(){
44451         if(this.autoHide !== false){
44452             if(!this.autoHideHd){
44453                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44454                 this.autoHideHd = {
44455                     "mouseout": function(e){
44456                         if(!e.within(this.el, true)){
44457                             st.delay(500);
44458                         }
44459                     },
44460                     "mouseover" : function(e){
44461                         st.cancel();
44462                     },
44463                     scope : this
44464                 };
44465             }
44466             this.el.on(this.autoHideHd);
44467         }
44468     },
44469
44470     clearAutoHide : function(){
44471         if(this.autoHide !== false){
44472             this.el.un("mouseout", this.autoHideHd.mouseout);
44473             this.el.un("mouseover", this.autoHideHd.mouseover);
44474         }
44475     },
44476
44477     clearMonitor : function(){
44478         Roo.get(document).un("click", this.slideInIf, this);
44479     },
44480
44481     // these names are backwards but not changed for compat
44482     slideOut : function(){
44483         if(this.isSlid || this.el.hasActiveFx()){
44484             return;
44485         }
44486         this.isSlid = true;
44487         if(this.collapseBtn){
44488             this.collapseBtn.hide();
44489         }
44490         this.closeBtnState = this.closeBtn.getStyle('display');
44491         this.closeBtn.hide();
44492         if(this.stickBtn){
44493             this.stickBtn.show();
44494         }
44495         this.el.show();
44496         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44497         this.beforeSlide();
44498         this.el.setStyle("z-index", 10001);
44499         this.el.slideIn(this.getSlideAnchor(), {
44500             callback: function(){
44501                 this.afterSlide();
44502                 this.initAutoHide();
44503                 Roo.get(document).on("click", this.slideInIf, this);
44504                 this.fireEvent("slideshow", this);
44505             },
44506             scope: this,
44507             block: true
44508         });
44509     },
44510
44511     afterSlideIn : function(){
44512         this.clearAutoHide();
44513         this.isSlid = false;
44514         this.clearMonitor();
44515         this.el.setStyle("z-index", "");
44516         if(this.collapseBtn){
44517             this.collapseBtn.show();
44518         }
44519         this.closeBtn.setStyle('display', this.closeBtnState);
44520         if(this.stickBtn){
44521             this.stickBtn.hide();
44522         }
44523         this.fireEvent("slidehide", this);
44524     },
44525
44526     slideIn : function(cb){
44527         if(!this.isSlid || this.el.hasActiveFx()){
44528             Roo.callback(cb);
44529             return;
44530         }
44531         this.isSlid = false;
44532         this.beforeSlide();
44533         this.el.slideOut(this.getSlideAnchor(), {
44534             callback: function(){
44535                 this.el.setLeftTop(-10000, -10000);
44536                 this.afterSlide();
44537                 this.afterSlideIn();
44538                 Roo.callback(cb);
44539             },
44540             scope: this,
44541             block: true
44542         });
44543     },
44544     
44545     slideInIf : function(e){
44546         if(!e.within(this.el)){
44547             this.slideIn();
44548         }
44549     },
44550
44551     animateCollapse : function(){
44552         this.beforeSlide();
44553         this.el.setStyle("z-index", 20000);
44554         var anchor = this.getSlideAnchor();
44555         this.el.slideOut(anchor, {
44556             callback : function(){
44557                 this.el.setStyle("z-index", "");
44558                 this.collapsedEl.slideIn(anchor, {duration:.3});
44559                 this.afterSlide();
44560                 this.el.setLocation(-10000,-10000);
44561                 this.el.hide();
44562                 this.fireEvent("collapsed", this);
44563             },
44564             scope: this,
44565             block: true
44566         });
44567     },
44568
44569     animateExpand : function(){
44570         this.beforeSlide();
44571         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44572         this.el.setStyle("z-index", 20000);
44573         this.collapsedEl.hide({
44574             duration:.1
44575         });
44576         this.el.slideIn(this.getSlideAnchor(), {
44577             callback : function(){
44578                 this.el.setStyle("z-index", "");
44579                 this.afterSlide();
44580                 if(this.split){
44581                     this.split.el.show();
44582                 }
44583                 this.fireEvent("invalidated", this);
44584                 this.fireEvent("expanded", this);
44585             },
44586             scope: this,
44587             block: true
44588         });
44589     },
44590
44591     anchors : {
44592         "west" : "left",
44593         "east" : "right",
44594         "north" : "top",
44595         "south" : "bottom"
44596     },
44597
44598     sanchors : {
44599         "west" : "l",
44600         "east" : "r",
44601         "north" : "t",
44602         "south" : "b"
44603     },
44604
44605     canchors : {
44606         "west" : "tl-tr",
44607         "east" : "tr-tl",
44608         "north" : "tl-bl",
44609         "south" : "bl-tl"
44610     },
44611
44612     getAnchor : function(){
44613         return this.anchors[this.position];
44614     },
44615
44616     getCollapseAnchor : function(){
44617         return this.canchors[this.position];
44618     },
44619
44620     getSlideAnchor : function(){
44621         return this.sanchors[this.position];
44622     },
44623
44624     getAlignAdj : function(){
44625         var cm = this.cmargins;
44626         switch(this.position){
44627             case "west":
44628                 return [0, 0];
44629             break;
44630             case "east":
44631                 return [0, 0];
44632             break;
44633             case "north":
44634                 return [0, 0];
44635             break;
44636             case "south":
44637                 return [0, 0];
44638             break;
44639         }
44640     },
44641
44642     getExpandAdj : function(){
44643         var c = this.collapsedEl, cm = this.cmargins;
44644         switch(this.position){
44645             case "west":
44646                 return [-(cm.right+c.getWidth()+cm.left), 0];
44647             break;
44648             case "east":
44649                 return [cm.right+c.getWidth()+cm.left, 0];
44650             break;
44651             case "north":
44652                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44653             break;
44654             case "south":
44655                 return [0, cm.top+cm.bottom+c.getHeight()];
44656             break;
44657         }
44658     }
44659 });/*
44660  * Based on:
44661  * Ext JS Library 1.1.1
44662  * Copyright(c) 2006-2007, Ext JS, LLC.
44663  *
44664  * Originally Released Under LGPL - original licence link has changed is not relivant.
44665  *
44666  * Fork - LGPL
44667  * <script type="text/javascript">
44668  */
44669 /*
44670  * These classes are private internal classes
44671  */
44672 Roo.bootstrap.layout.Center = function(config){
44673     config.region = "center";
44674     Roo.bootstrap.layout.Region.call(this, config);
44675     this.visible = true;
44676     this.minWidth = config.minWidth || 20;
44677     this.minHeight = config.minHeight || 20;
44678 };
44679
44680 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44681     hide : function(){
44682         // center panel can't be hidden
44683     },
44684     
44685     show : function(){
44686         // center panel can't be hidden
44687     },
44688     
44689     getMinWidth: function(){
44690         return this.minWidth;
44691     },
44692     
44693     getMinHeight: function(){
44694         return this.minHeight;
44695     }
44696 });
44697
44698
44699
44700
44701  
44702
44703
44704
44705
44706
44707
44708 Roo.bootstrap.layout.North = function(config)
44709 {
44710     config.region = 'north';
44711     config.cursor = 'n-resize';
44712     
44713     Roo.bootstrap.layout.Split.call(this, config);
44714     
44715     
44716     if(this.split){
44717         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44718         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44719         this.split.el.addClass("roo-layout-split-v");
44720     }
44721     //var size = config.initialSize || config.height;
44722     //if(this.el && typeof size != "undefined"){
44723     //    this.el.setHeight(size);
44724     //}
44725 };
44726 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44727 {
44728     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44729      
44730      
44731     onRender : function(ctr, pos)
44732     {
44733         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44734         var size = this.config.initialSize || this.config.height;
44735         if(this.el && typeof size != "undefined"){
44736             this.el.setHeight(size);
44737         }
44738     
44739     },
44740     
44741     getBox : function(){
44742         if(this.collapsed){
44743             return this.collapsedEl.getBox();
44744         }
44745         var box = this.el.getBox();
44746         if(this.split){
44747             box.height += this.split.el.getHeight();
44748         }
44749         return box;
44750     },
44751     
44752     updateBox : function(box){
44753         if(this.split && !this.collapsed){
44754             box.height -= this.split.el.getHeight();
44755             this.split.el.setLeft(box.x);
44756             this.split.el.setTop(box.y+box.height);
44757             this.split.el.setWidth(box.width);
44758         }
44759         if(this.collapsed){
44760             this.updateBody(box.width, null);
44761         }
44762         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44763     }
44764 });
44765
44766
44767
44768
44769
44770 Roo.bootstrap.layout.South = function(config){
44771     config.region = 'south';
44772     config.cursor = 's-resize';
44773     Roo.bootstrap.layout.Split.call(this, config);
44774     if(this.split){
44775         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44776         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44777         this.split.el.addClass("roo-layout-split-v");
44778     }
44779     
44780 };
44781
44782 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44783     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44784     
44785     onRender : function(ctr, pos)
44786     {
44787         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44788         var size = this.config.initialSize || this.config.height;
44789         if(this.el && typeof size != "undefined"){
44790             this.el.setHeight(size);
44791         }
44792     
44793     },
44794     
44795     getBox : function(){
44796         if(this.collapsed){
44797             return this.collapsedEl.getBox();
44798         }
44799         var box = this.el.getBox();
44800         if(this.split){
44801             var sh = this.split.el.getHeight();
44802             box.height += sh;
44803             box.y -= sh;
44804         }
44805         return box;
44806     },
44807     
44808     updateBox : function(box){
44809         if(this.split && !this.collapsed){
44810             var sh = this.split.el.getHeight();
44811             box.height -= sh;
44812             box.y += sh;
44813             this.split.el.setLeft(box.x);
44814             this.split.el.setTop(box.y-sh);
44815             this.split.el.setWidth(box.width);
44816         }
44817         if(this.collapsed){
44818             this.updateBody(box.width, null);
44819         }
44820         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44821     }
44822 });
44823
44824 Roo.bootstrap.layout.East = function(config){
44825     config.region = "east";
44826     config.cursor = "e-resize";
44827     Roo.bootstrap.layout.Split.call(this, config);
44828     if(this.split){
44829         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44830         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44831         this.split.el.addClass("roo-layout-split-h");
44832     }
44833     
44834 };
44835 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44836     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44837     
44838     onRender : function(ctr, pos)
44839     {
44840         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44841         var size = this.config.initialSize || this.config.width;
44842         if(this.el && typeof size != "undefined"){
44843             this.el.setWidth(size);
44844         }
44845     
44846     },
44847     
44848     getBox : function(){
44849         if(this.collapsed){
44850             return this.collapsedEl.getBox();
44851         }
44852         var box = this.el.getBox();
44853         if(this.split){
44854             var sw = this.split.el.getWidth();
44855             box.width += sw;
44856             box.x -= sw;
44857         }
44858         return box;
44859     },
44860
44861     updateBox : function(box){
44862         if(this.split && !this.collapsed){
44863             var sw = this.split.el.getWidth();
44864             box.width -= sw;
44865             this.split.el.setLeft(box.x);
44866             this.split.el.setTop(box.y);
44867             this.split.el.setHeight(box.height);
44868             box.x += sw;
44869         }
44870         if(this.collapsed){
44871             this.updateBody(null, box.height);
44872         }
44873         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44874     }
44875 });
44876
44877 Roo.bootstrap.layout.West = function(config){
44878     config.region = "west";
44879     config.cursor = "w-resize";
44880     
44881     Roo.bootstrap.layout.Split.call(this, config);
44882     if(this.split){
44883         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44884         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44885         this.split.el.addClass("roo-layout-split-h");
44886     }
44887     
44888 };
44889 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44890     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44891     
44892     onRender: function(ctr, pos)
44893     {
44894         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44895         var size = this.config.initialSize || this.config.width;
44896         if(typeof size != "undefined"){
44897             this.el.setWidth(size);
44898         }
44899     },
44900     
44901     getBox : function(){
44902         if(this.collapsed){
44903             return this.collapsedEl.getBox();
44904         }
44905         var box = this.el.getBox();
44906         if (box.width == 0) {
44907             box.width = this.config.width; // kludge?
44908         }
44909         if(this.split){
44910             box.width += this.split.el.getWidth();
44911         }
44912         return box;
44913     },
44914     
44915     updateBox : function(box){
44916         if(this.split && !this.collapsed){
44917             var sw = this.split.el.getWidth();
44918             box.width -= sw;
44919             this.split.el.setLeft(box.x+box.width);
44920             this.split.el.setTop(box.y);
44921             this.split.el.setHeight(box.height);
44922         }
44923         if(this.collapsed){
44924             this.updateBody(null, box.height);
44925         }
44926         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44927     }
44928 });/*
44929  * Based on:
44930  * Ext JS Library 1.1.1
44931  * Copyright(c) 2006-2007, Ext JS, LLC.
44932  *
44933  * Originally Released Under LGPL - original licence link has changed is not relivant.
44934  *
44935  * Fork - LGPL
44936  * <script type="text/javascript">
44937  */
44938 /**
44939  * @class Roo.bootstrap.paenl.Content
44940  * @extends Roo.util.Observable
44941  * @children Roo.bootstrap.Component
44942  * @parent builder Roo.bootstrap.layout.Border
44943  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44944  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44945  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44946  * @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
44947  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44948  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44949  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44950  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44951  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44952  * @cfg {String} title          The title for this panel
44953  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44954  * @cfg {String} url            Calls {@link #setUrl} with this value
44955  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44956  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44957  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44958  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44959  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44960  * @cfg {Boolean} badges render the badges
44961  * @cfg {String} cls  extra classes to use  
44962  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44963  
44964  * @constructor
44965  * Create a new ContentPanel.
44966  * @param {String/Object} config A string to set only the title or a config object
44967  
44968  */
44969 Roo.bootstrap.panel.Content = function( config){
44970     
44971     this.tpl = config.tpl || false;
44972     
44973     var el = config.el;
44974     var content = config.content;
44975
44976     if(config.autoCreate){ // xtype is available if this is called from factory
44977         el = Roo.id();
44978     }
44979     this.el = Roo.get(el);
44980     if(!this.el && config && config.autoCreate){
44981         if(typeof config.autoCreate == "object"){
44982             if(!config.autoCreate.id){
44983                 config.autoCreate.id = config.id||el;
44984             }
44985             this.el = Roo.DomHelper.append(document.body,
44986                         config.autoCreate, true);
44987         }else{
44988             var elcfg =  {
44989                 tag: "div",
44990                 cls: (config.cls || '') +
44991                     (config.background ? ' bg-' + config.background : '') +
44992                     " roo-layout-inactive-content",
44993                 id: config.id||el
44994             };
44995             if (config.iframe) {
44996                 elcfg.cn = [
44997                     {
44998                         tag : 'iframe',
44999                         style : 'border: 0px',
45000                         src : 'about:blank'
45001                     }
45002                 ];
45003             }
45004               
45005             if (config.html) {
45006                 elcfg.html = config.html;
45007                 
45008             }
45009                         
45010             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45011             if (config.iframe) {
45012                 this.iframeEl = this.el.select('iframe',true).first();
45013             }
45014             
45015         }
45016     } 
45017     this.closable = false;
45018     this.loaded = false;
45019     this.active = false;
45020    
45021       
45022     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45023         
45024         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45025         
45026         this.wrapEl = this.el; //this.el.wrap();
45027         var ti = [];
45028         if (config.toolbar.items) {
45029             ti = config.toolbar.items ;
45030             delete config.toolbar.items ;
45031         }
45032         
45033         var nitems = [];
45034         this.toolbar.render(this.wrapEl, 'before');
45035         for(var i =0;i < ti.length;i++) {
45036           //  Roo.log(['add child', items[i]]);
45037             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45038         }
45039         this.toolbar.items = nitems;
45040         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45041         delete config.toolbar;
45042         
45043     }
45044     /*
45045     // xtype created footer. - not sure if will work as we normally have to render first..
45046     if (this.footer && !this.footer.el && this.footer.xtype) {
45047         if (!this.wrapEl) {
45048             this.wrapEl = this.el.wrap();
45049         }
45050     
45051         this.footer.container = this.wrapEl.createChild();
45052          
45053         this.footer = Roo.factory(this.footer, Roo);
45054         
45055     }
45056     */
45057     
45058      if(typeof config == "string"){
45059         this.title = config;
45060     }else{
45061         Roo.apply(this, config);
45062     }
45063     
45064     if(this.resizeEl){
45065         this.resizeEl = Roo.get(this.resizeEl, true);
45066     }else{
45067         this.resizeEl = this.el;
45068     }
45069     // handle view.xtype
45070     
45071  
45072     
45073     
45074     this.addEvents({
45075         /**
45076          * @event activate
45077          * Fires when this panel is activated. 
45078          * @param {Roo.ContentPanel} this
45079          */
45080         "activate" : true,
45081         /**
45082          * @event deactivate
45083          * Fires when this panel is activated. 
45084          * @param {Roo.ContentPanel} this
45085          */
45086         "deactivate" : true,
45087
45088         /**
45089          * @event resize
45090          * Fires when this panel is resized if fitToFrame is true.
45091          * @param {Roo.ContentPanel} this
45092          * @param {Number} width The width after any component adjustments
45093          * @param {Number} height The height after any component adjustments
45094          */
45095         "resize" : true,
45096         
45097          /**
45098          * @event render
45099          * Fires when this tab is created
45100          * @param {Roo.ContentPanel} this
45101          */
45102         "render" : true,
45103         
45104           /**
45105          * @event scroll
45106          * Fires when this content is scrolled
45107          * @param {Roo.ContentPanel} this
45108          * @param {Event} scrollEvent
45109          */
45110         "scroll" : true
45111         
45112         
45113         
45114     });
45115     
45116
45117     
45118     
45119     if(this.autoScroll && !this.iframe){
45120         this.resizeEl.setStyle("overflow", "auto");
45121         this.resizeEl.on('scroll', this.onScroll, this);
45122     } else {
45123         // fix randome scrolling
45124         //this.el.on('scroll', function() {
45125         //    Roo.log('fix random scolling');
45126         //    this.scrollTo('top',0); 
45127         //});
45128     }
45129     content = content || this.content;
45130     if(content){
45131         this.setContent(content);
45132     }
45133     if(config && config.url){
45134         this.setUrl(this.url, this.params, this.loadOnce);
45135     }
45136     
45137     
45138     
45139     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45140     
45141     if (this.view && typeof(this.view.xtype) != 'undefined') {
45142         this.view.el = this.el.appendChild(document.createElement("div"));
45143         this.view = Roo.factory(this.view); 
45144         this.view.render  &&  this.view.render(false, '');  
45145     }
45146     
45147     
45148     this.fireEvent('render', this);
45149 };
45150
45151 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45152     
45153     cls : '',
45154     background : '',
45155     
45156     tabTip : '',
45157     
45158     iframe : false,
45159     iframeEl : false,
45160     
45161     /* Resize Element - use this to work out scroll etc. */
45162     resizeEl : false,
45163     
45164     setRegion : function(region){
45165         this.region = region;
45166         this.setActiveClass(region && !this.background);
45167     },
45168     
45169     
45170     setActiveClass: function(state)
45171     {
45172         if(state){
45173            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45174            this.el.setStyle('position','relative');
45175         }else{
45176            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45177            this.el.setStyle('position', 'absolute');
45178         } 
45179     },
45180     
45181     /**
45182      * Returns the toolbar for this Panel if one was configured. 
45183      * @return {Roo.Toolbar} 
45184      */
45185     getToolbar : function(){
45186         return this.toolbar;
45187     },
45188     
45189     setActiveState : function(active)
45190     {
45191         this.active = active;
45192         this.setActiveClass(active);
45193         if(!active){
45194             if(this.fireEvent("deactivate", this) === false){
45195                 return false;
45196             }
45197             return true;
45198         }
45199         this.fireEvent("activate", this);
45200         return true;
45201     },
45202     /**
45203      * Updates this panel's element (not for iframe)
45204      * @param {String} content The new content
45205      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45206     */
45207     setContent : function(content, loadScripts){
45208         if (this.iframe) {
45209             return;
45210         }
45211         
45212         this.el.update(content, loadScripts);
45213     },
45214
45215     ignoreResize : function(w, h)
45216     {
45217         //return false; // always resize?
45218         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45219             return true;
45220         }else{
45221             this.lastSize = {width: w, height: h};
45222             return false;
45223         }
45224     },
45225     /**
45226      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45227      * @return {Roo.UpdateManager} The UpdateManager
45228      */
45229     getUpdateManager : function(){
45230         if (this.iframe) {
45231             return false;
45232         }
45233         return this.el.getUpdateManager();
45234     },
45235      /**
45236      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45237      * Does not work with IFRAME contents
45238      * @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:
45239 <pre><code>
45240 panel.load({
45241     url: "your-url.php",
45242     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45243     callback: yourFunction,
45244     scope: yourObject, //(optional scope)
45245     discardUrl: false,
45246     nocache: false,
45247     text: "Loading...",
45248     timeout: 30,
45249     scripts: false
45250 });
45251 </code></pre>
45252      
45253      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45254      * 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.
45255      * @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}
45256      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45257      * @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.
45258      * @return {Roo.ContentPanel} this
45259      */
45260     load : function(){
45261         
45262         if (this.iframe) {
45263             return this;
45264         }
45265         
45266         var um = this.el.getUpdateManager();
45267         um.update.apply(um, arguments);
45268         return this;
45269     },
45270
45271
45272     /**
45273      * 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.
45274      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45275      * @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)
45276      * @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)
45277      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45278      */
45279     setUrl : function(url, params, loadOnce){
45280         if (this.iframe) {
45281             this.iframeEl.dom.src = url;
45282             return false;
45283         }
45284         
45285         if(this.refreshDelegate){
45286             this.removeListener("activate", this.refreshDelegate);
45287         }
45288         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45289         this.on("activate", this.refreshDelegate);
45290         return this.el.getUpdateManager();
45291     },
45292     
45293     _handleRefresh : function(url, params, loadOnce){
45294         if(!loadOnce || !this.loaded){
45295             var updater = this.el.getUpdateManager();
45296             updater.update(url, params, this._setLoaded.createDelegate(this));
45297         }
45298     },
45299     
45300     _setLoaded : function(){
45301         this.loaded = true;
45302     }, 
45303     
45304     /**
45305      * Returns this panel's id
45306      * @return {String} 
45307      */
45308     getId : function(){
45309         return this.el.id;
45310     },
45311     
45312     /** 
45313      * Returns this panel's element - used by regiosn to add.
45314      * @return {Roo.Element} 
45315      */
45316     getEl : function(){
45317         return this.wrapEl || this.el;
45318     },
45319     
45320    
45321     
45322     adjustForComponents : function(width, height)
45323     {
45324         //Roo.log('adjustForComponents ');
45325         if(this.resizeEl != this.el){
45326             width -= this.el.getFrameWidth('lr');
45327             height -= this.el.getFrameWidth('tb');
45328         }
45329         if(this.toolbar){
45330             var te = this.toolbar.getEl();
45331             te.setWidth(width);
45332             height -= te.getHeight();
45333         }
45334         if(this.footer){
45335             var te = this.footer.getEl();
45336             te.setWidth(width);
45337             height -= te.getHeight();
45338         }
45339         
45340         
45341         if(this.adjustments){
45342             width += this.adjustments[0];
45343             height += this.adjustments[1];
45344         }
45345         return {"width": width, "height": height};
45346     },
45347     
45348     setSize : function(width, height){
45349         if(this.fitToFrame && !this.ignoreResize(width, height)){
45350             if(this.fitContainer && this.resizeEl != this.el){
45351                 this.el.setSize(width, height);
45352             }
45353             var size = this.adjustForComponents(width, height);
45354             if (this.iframe) {
45355                 this.iframeEl.setSize(width,height);
45356             }
45357             
45358             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45359             this.fireEvent('resize', this, size.width, size.height);
45360             
45361             
45362         }
45363     },
45364     
45365     /**
45366      * Returns this panel's title
45367      * @return {String} 
45368      */
45369     getTitle : function(){
45370         
45371         if (typeof(this.title) != 'object') {
45372             return this.title;
45373         }
45374         
45375         var t = '';
45376         for (var k in this.title) {
45377             if (!this.title.hasOwnProperty(k)) {
45378                 continue;
45379             }
45380             
45381             if (k.indexOf('-') >= 0) {
45382                 var s = k.split('-');
45383                 for (var i = 0; i<s.length; i++) {
45384                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45385                 }
45386             } else {
45387                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45388             }
45389         }
45390         return t;
45391     },
45392     
45393     /**
45394      * Set this panel's title
45395      * @param {String} title
45396      */
45397     setTitle : function(title){
45398         this.title = title;
45399         if(this.region){
45400             this.region.updatePanelTitle(this, title);
45401         }
45402     },
45403     
45404     /**
45405      * Returns true is this panel was configured to be closable
45406      * @return {Boolean} 
45407      */
45408     isClosable : function(){
45409         return this.closable;
45410     },
45411     
45412     beforeSlide : function(){
45413         this.el.clip();
45414         this.resizeEl.clip();
45415     },
45416     
45417     afterSlide : function(){
45418         this.el.unclip();
45419         this.resizeEl.unclip();
45420     },
45421     
45422     /**
45423      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45424      *   Will fail silently if the {@link #setUrl} method has not been called.
45425      *   This does not activate the panel, just updates its content.
45426      */
45427     refresh : function(){
45428         if(this.refreshDelegate){
45429            this.loaded = false;
45430            this.refreshDelegate();
45431         }
45432     },
45433     
45434     /**
45435      * Destroys this panel
45436      */
45437     destroy : function(){
45438         this.el.removeAllListeners();
45439         var tempEl = document.createElement("span");
45440         tempEl.appendChild(this.el.dom);
45441         tempEl.innerHTML = "";
45442         this.el.remove();
45443         this.el = null;
45444     },
45445     
45446     /**
45447      * form - if the content panel contains a form - this is a reference to it.
45448      * @type {Roo.form.Form}
45449      */
45450     form : false,
45451     /**
45452      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45453      *    This contains a reference to it.
45454      * @type {Roo.View}
45455      */
45456     view : false,
45457     
45458       /**
45459      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45460      * <pre><code>
45461
45462 layout.addxtype({
45463        xtype : 'Form',
45464        items: [ .... ]
45465    }
45466 );
45467
45468 </code></pre>
45469      * @param {Object} cfg Xtype definition of item to add.
45470      */
45471     
45472     
45473     getChildContainer: function () {
45474         return this.getEl();
45475     },
45476     
45477     
45478     onScroll : function(e)
45479     {
45480         this.fireEvent('scroll', this, e);
45481     }
45482     
45483     
45484     /*
45485         var  ret = new Roo.factory(cfg);
45486         return ret;
45487         
45488         
45489         // add form..
45490         if (cfg.xtype.match(/^Form$/)) {
45491             
45492             var el;
45493             //if (this.footer) {
45494             //    el = this.footer.container.insertSibling(false, 'before');
45495             //} else {
45496                 el = this.el.createChild();
45497             //}
45498
45499             this.form = new  Roo.form.Form(cfg);
45500             
45501             
45502             if ( this.form.allItems.length) {
45503                 this.form.render(el.dom);
45504             }
45505             return this.form;
45506         }
45507         // should only have one of theses..
45508         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45509             // views.. should not be just added - used named prop 'view''
45510             
45511             cfg.el = this.el.appendChild(document.createElement("div"));
45512             // factory?
45513             
45514             var ret = new Roo.factory(cfg);
45515              
45516              ret.render && ret.render(false, ''); // render blank..
45517             this.view = ret;
45518             return ret;
45519         }
45520         return false;
45521     }
45522     \*/
45523 });
45524  
45525 /**
45526  * @class Roo.bootstrap.panel.Grid
45527  * @extends Roo.bootstrap.panel.Content
45528  * @constructor
45529  * Create a new GridPanel.
45530  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45531  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45532  * @param {Object} config A the config object
45533   
45534  */
45535
45536
45537
45538 Roo.bootstrap.panel.Grid = function(config)
45539 {
45540     
45541       
45542     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45543         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45544
45545     config.el = this.wrapper;
45546     //this.el = this.wrapper;
45547     
45548       if (config.container) {
45549         // ctor'ed from a Border/panel.grid
45550         
45551         
45552         this.wrapper.setStyle("overflow", "hidden");
45553         this.wrapper.addClass('roo-grid-container');
45554
45555     }
45556     
45557     
45558     if(config.toolbar){
45559         var tool_el = this.wrapper.createChild();    
45560         this.toolbar = Roo.factory(config.toolbar);
45561         var ti = [];
45562         if (config.toolbar.items) {
45563             ti = config.toolbar.items ;
45564             delete config.toolbar.items ;
45565         }
45566         
45567         var nitems = [];
45568         this.toolbar.render(tool_el);
45569         for(var i =0;i < ti.length;i++) {
45570           //  Roo.log(['add child', items[i]]);
45571             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45572         }
45573         this.toolbar.items = nitems;
45574         
45575         delete config.toolbar;
45576     }
45577     
45578     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45579     config.grid.scrollBody = true;;
45580     config.grid.monitorWindowResize = false; // turn off autosizing
45581     config.grid.autoHeight = false;
45582     config.grid.autoWidth = false;
45583     
45584     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45585     
45586     if (config.background) {
45587         // render grid on panel activation (if panel background)
45588         this.on('activate', function(gp) {
45589             if (!gp.grid.rendered) {
45590                 gp.grid.render(this.wrapper);
45591                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45592             }
45593         });
45594             
45595     } else {
45596         this.grid.render(this.wrapper);
45597         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45598
45599     }
45600     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45601     // ??? needed ??? config.el = this.wrapper;
45602     
45603     
45604     
45605   
45606     // xtype created footer. - not sure if will work as we normally have to render first..
45607     if (this.footer && !this.footer.el && this.footer.xtype) {
45608         
45609         var ctr = this.grid.getView().getFooterPanel(true);
45610         this.footer.dataSource = this.grid.dataSource;
45611         this.footer = Roo.factory(this.footer, Roo);
45612         this.footer.render(ctr);
45613         
45614     }
45615     
45616     
45617     
45618     
45619      
45620 };
45621
45622 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45623 {
45624   
45625     getId : function(){
45626         return this.grid.id;
45627     },
45628     
45629     /**
45630      * Returns the grid for this panel
45631      * @return {Roo.bootstrap.Table} 
45632      */
45633     getGrid : function(){
45634         return this.grid;    
45635     },
45636     
45637     setSize : function(width, height)
45638     {
45639      
45640         //if(!this.ignoreResize(width, height)){
45641             var grid = this.grid;
45642             var size = this.adjustForComponents(width, height);
45643             // tfoot is not a footer?
45644           
45645             
45646             var gridel = grid.getGridEl();
45647             gridel.setSize(size.width, size.height);
45648             
45649             var tbd = grid.getGridEl().select('tbody', true).first();
45650             var thd = grid.getGridEl().select('thead',true).first();
45651             var tbf= grid.getGridEl().select('tfoot', true).first();
45652
45653             if (tbf) {
45654                 size.height -= tbf.getHeight();
45655             }
45656             if (thd) {
45657                 size.height -= thd.getHeight();
45658             }
45659             
45660             tbd.setSize(size.width, size.height );
45661             // this is for the account management tab -seems to work there.
45662             var thd = grid.getGridEl().select('thead',true).first();
45663             //if (tbd) {
45664             //    tbd.setSize(size.width, size.height - thd.getHeight());
45665             //}
45666              
45667             grid.autoSize();
45668         //}
45669    
45670     },
45671      
45672     
45673     
45674     beforeSlide : function(){
45675         this.grid.getView().scroller.clip();
45676     },
45677     
45678     afterSlide : function(){
45679         this.grid.getView().scroller.unclip();
45680     },
45681     
45682     destroy : function(){
45683         this.grid.destroy();
45684         delete this.grid;
45685         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45686     }
45687 });
45688
45689 /**
45690  * @class Roo.bootstrap.panel.Nest
45691  * @extends Roo.bootstrap.panel.Content
45692  * @constructor
45693  * Create a new Panel, that can contain a layout.Border.
45694  * 
45695  * 
45696  * @param {String/Object} config A string to set only the title or a config object
45697  */
45698 Roo.bootstrap.panel.Nest = function(config)
45699 {
45700     // construct with only one argument..
45701     /* FIXME - implement nicer consturctors
45702     if (layout.layout) {
45703         config = layout;
45704         layout = config.layout;
45705         delete config.layout;
45706     }
45707     if (layout.xtype && !layout.getEl) {
45708         // then layout needs constructing..
45709         layout = Roo.factory(layout, Roo);
45710     }
45711     */
45712     
45713     config.el =  config.layout.getEl();
45714     
45715     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45716     
45717     config.layout.monitorWindowResize = false; // turn off autosizing
45718     this.layout = config.layout;
45719     this.layout.getEl().addClass("roo-layout-nested-layout");
45720     this.layout.parent = this;
45721     
45722     
45723     
45724     
45725 };
45726
45727 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45728     /**
45729     * @cfg {Roo.BorderLayout} layout The layout for this panel
45730     */
45731     layout : false,
45732
45733     setSize : function(width, height){
45734         if(!this.ignoreResize(width, height)){
45735             var size = this.adjustForComponents(width, height);
45736             var el = this.layout.getEl();
45737             if (size.height < 1) {
45738                 el.setWidth(size.width);   
45739             } else {
45740                 el.setSize(size.width, size.height);
45741             }
45742             var touch = el.dom.offsetWidth;
45743             this.layout.layout();
45744             // ie requires a double layout on the first pass
45745             if(Roo.isIE && !this.initialized){
45746                 this.initialized = true;
45747                 this.layout.layout();
45748             }
45749         }
45750     },
45751     
45752     // activate all subpanels if not currently active..
45753     
45754     setActiveState : function(active){
45755         this.active = active;
45756         this.setActiveClass(active);
45757         
45758         if(!active){
45759             this.fireEvent("deactivate", this);
45760             return;
45761         }
45762         
45763         this.fireEvent("activate", this);
45764         // not sure if this should happen before or after..
45765         if (!this.layout) {
45766             return; // should not happen..
45767         }
45768         var reg = false;
45769         for (var r in this.layout.regions) {
45770             reg = this.layout.getRegion(r);
45771             if (reg.getActivePanel()) {
45772                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45773                 reg.setActivePanel(reg.getActivePanel());
45774                 continue;
45775             }
45776             if (!reg.panels.length) {
45777                 continue;
45778             }
45779             reg.showPanel(reg.getPanel(0));
45780         }
45781         
45782         
45783         
45784         
45785     },
45786     
45787     /**
45788      * Returns the nested BorderLayout for this panel
45789      * @return {Roo.BorderLayout} 
45790      */
45791     getLayout : function(){
45792         return this.layout;
45793     },
45794     
45795      /**
45796      * Adds a xtype elements to the layout of the nested panel
45797      * <pre><code>
45798
45799 panel.addxtype({
45800        xtype : 'ContentPanel',
45801        region: 'west',
45802        items: [ .... ]
45803    }
45804 );
45805
45806 panel.addxtype({
45807         xtype : 'NestedLayoutPanel',
45808         region: 'west',
45809         layout: {
45810            center: { },
45811            west: { }   
45812         },
45813         items : [ ... list of content panels or nested layout panels.. ]
45814    }
45815 );
45816 </code></pre>
45817      * @param {Object} cfg Xtype definition of item to add.
45818      */
45819     addxtype : function(cfg) {
45820         return this.layout.addxtype(cfg);
45821     
45822     }
45823 });/*
45824  * Based on:
45825  * Ext JS Library 1.1.1
45826  * Copyright(c) 2006-2007, Ext JS, LLC.
45827  *
45828  * Originally Released Under LGPL - original licence link has changed is not relivant.
45829  *
45830  * Fork - LGPL
45831  * <script type="text/javascript">
45832  */
45833 /**
45834  * @class Roo.TabPanel
45835  * @extends Roo.util.Observable
45836  * A lightweight tab container.
45837  * <br><br>
45838  * Usage:
45839  * <pre><code>
45840 // basic tabs 1, built from existing content
45841 var tabs = new Roo.TabPanel("tabs1");
45842 tabs.addTab("script", "View Script");
45843 tabs.addTab("markup", "View Markup");
45844 tabs.activate("script");
45845
45846 // more advanced tabs, built from javascript
45847 var jtabs = new Roo.TabPanel("jtabs");
45848 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45849
45850 // set up the UpdateManager
45851 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45852 var updater = tab2.getUpdateManager();
45853 updater.setDefaultUrl("ajax1.htm");
45854 tab2.on('activate', updater.refresh, updater, true);
45855
45856 // Use setUrl for Ajax loading
45857 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45858 tab3.setUrl("ajax2.htm", null, true);
45859
45860 // Disabled tab
45861 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45862 tab4.disable();
45863
45864 jtabs.activate("jtabs-1");
45865  * </code></pre>
45866  * @constructor
45867  * Create a new TabPanel.
45868  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45869  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45870  */
45871 Roo.bootstrap.panel.Tabs = function(config){
45872     /**
45873     * The container element for this TabPanel.
45874     * @type Roo.Element
45875     */
45876     this.el = Roo.get(config.el);
45877     delete config.el;
45878     if(config){
45879         if(typeof config == "boolean"){
45880             this.tabPosition = config ? "bottom" : "top";
45881         }else{
45882             Roo.apply(this, config);
45883         }
45884     }
45885     
45886     if(this.tabPosition == "bottom"){
45887         // if tabs are at the bottom = create the body first.
45888         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45889         this.el.addClass("roo-tabs-bottom");
45890     }
45891     // next create the tabs holders
45892     
45893     if (this.tabPosition == "west"){
45894         
45895         var reg = this.region; // fake it..
45896         while (reg) {
45897             if (!reg.mgr.parent) {
45898                 break;
45899             }
45900             reg = reg.mgr.parent.region;
45901         }
45902         Roo.log("got nest?");
45903         Roo.log(reg);
45904         if (reg.mgr.getRegion('west')) {
45905             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45906             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45907             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45908             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45909             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45910         
45911             
45912         }
45913         
45914         
45915     } else {
45916      
45917         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45918         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45919         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45920         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45921     }
45922     
45923     
45924     if(Roo.isIE){
45925         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45926     }
45927     
45928     // finally - if tabs are at the top, then create the body last..
45929     if(this.tabPosition != "bottom"){
45930         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45931          * @type Roo.Element
45932          */
45933         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45934         this.el.addClass("roo-tabs-top");
45935     }
45936     this.items = [];
45937
45938     this.bodyEl.setStyle("position", "relative");
45939
45940     this.active = null;
45941     this.activateDelegate = this.activate.createDelegate(this);
45942
45943     this.addEvents({
45944         /**
45945          * @event tabchange
45946          * Fires when the active tab changes
45947          * @param {Roo.TabPanel} this
45948          * @param {Roo.TabPanelItem} activePanel The new active tab
45949          */
45950         "tabchange": true,
45951         /**
45952          * @event beforetabchange
45953          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45954          * @param {Roo.TabPanel} this
45955          * @param {Object} e Set cancel to true on this object to cancel the tab change
45956          * @param {Roo.TabPanelItem} tab The tab being changed to
45957          */
45958         "beforetabchange" : true
45959     });
45960
45961     Roo.EventManager.onWindowResize(this.onResize, this);
45962     this.cpad = this.el.getPadding("lr");
45963     this.hiddenCount = 0;
45964
45965
45966     // toolbar on the tabbar support...
45967     if (this.toolbar) {
45968         alert("no toolbar support yet");
45969         this.toolbar  = false;
45970         /*
45971         var tcfg = this.toolbar;
45972         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45973         this.toolbar = new Roo.Toolbar(tcfg);
45974         if (Roo.isSafari) {
45975             var tbl = tcfg.container.child('table', true);
45976             tbl.setAttribute('width', '100%');
45977         }
45978         */
45979         
45980     }
45981    
45982
45983
45984     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45985 };
45986
45987 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45988     /*
45989      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45990      */
45991     tabPosition : "top",
45992     /*
45993      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45994      */
45995     currentTabWidth : 0,
45996     /*
45997      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45998      */
45999     minTabWidth : 40,
46000     /*
46001      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46002      */
46003     maxTabWidth : 250,
46004     /*
46005      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46006      */
46007     preferredTabWidth : 175,
46008     /*
46009      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46010      */
46011     resizeTabs : false,
46012     /*
46013      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46014      */
46015     monitorResize : true,
46016     /*
46017      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46018      */
46019     toolbar : false,  // set by caller..
46020     
46021     region : false, /// set by caller
46022     
46023     disableTooltips : true, // not used yet...
46024
46025     /**
46026      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46027      * @param {String} id The id of the div to use <b>or create</b>
46028      * @param {String} text The text for the tab
46029      * @param {String} content (optional) Content to put in the TabPanelItem body
46030      * @param {Boolean} closable (optional) True to create a close icon on the tab
46031      * @return {Roo.TabPanelItem} The created TabPanelItem
46032      */
46033     addTab : function(id, text, content, closable, tpl)
46034     {
46035         var item = new Roo.bootstrap.panel.TabItem({
46036             panel: this,
46037             id : id,
46038             text : text,
46039             closable : closable,
46040             tpl : tpl
46041         });
46042         this.addTabItem(item);
46043         if(content){
46044             item.setContent(content);
46045         }
46046         return item;
46047     },
46048
46049     /**
46050      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46051      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46052      * @return {Roo.TabPanelItem}
46053      */
46054     getTab : function(id){
46055         return this.items[id];
46056     },
46057
46058     /**
46059      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46060      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46061      */
46062     hideTab : function(id){
46063         var t = this.items[id];
46064         if(!t.isHidden()){
46065            t.setHidden(true);
46066            this.hiddenCount++;
46067            this.autoSizeTabs();
46068         }
46069     },
46070
46071     /**
46072      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46073      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46074      */
46075     unhideTab : function(id){
46076         var t = this.items[id];
46077         if(t.isHidden()){
46078            t.setHidden(false);
46079            this.hiddenCount--;
46080            this.autoSizeTabs();
46081         }
46082     },
46083
46084     /**
46085      * Adds an existing {@link Roo.TabPanelItem}.
46086      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46087      */
46088     addTabItem : function(item)
46089     {
46090         this.items[item.id] = item;
46091         this.items.push(item);
46092         this.autoSizeTabs();
46093       //  if(this.resizeTabs){
46094     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46095   //         this.autoSizeTabs();
46096 //        }else{
46097 //            item.autoSize();
46098        // }
46099     },
46100
46101     /**
46102      * Removes a {@link Roo.TabPanelItem}.
46103      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46104      */
46105     removeTab : function(id){
46106         var items = this.items;
46107         var tab = items[id];
46108         if(!tab) { return; }
46109         var index = items.indexOf(tab);
46110         if(this.active == tab && items.length > 1){
46111             var newTab = this.getNextAvailable(index);
46112             if(newTab) {
46113                 newTab.activate();
46114             }
46115         }
46116         this.stripEl.dom.removeChild(tab.pnode.dom);
46117         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46118             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46119         }
46120         items.splice(index, 1);
46121         delete this.items[tab.id];
46122         tab.fireEvent("close", tab);
46123         tab.purgeListeners();
46124         this.autoSizeTabs();
46125     },
46126
46127     getNextAvailable : function(start){
46128         var items = this.items;
46129         var index = start;
46130         // look for a next tab that will slide over to
46131         // replace the one being removed
46132         while(index < items.length){
46133             var item = items[++index];
46134             if(item && !item.isHidden()){
46135                 return item;
46136             }
46137         }
46138         // if one isn't found select the previous tab (on the left)
46139         index = start;
46140         while(index >= 0){
46141             var item = items[--index];
46142             if(item && !item.isHidden()){
46143                 return item;
46144             }
46145         }
46146         return null;
46147     },
46148
46149     /**
46150      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46151      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46152      */
46153     disableTab : function(id){
46154         var tab = this.items[id];
46155         if(tab && this.active != tab){
46156             tab.disable();
46157         }
46158     },
46159
46160     /**
46161      * Enables a {@link Roo.TabPanelItem} that is disabled.
46162      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46163      */
46164     enableTab : function(id){
46165         var tab = this.items[id];
46166         tab.enable();
46167     },
46168
46169     /**
46170      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46171      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46172      * @return {Roo.TabPanelItem} The TabPanelItem.
46173      */
46174     activate : function(id)
46175     {
46176         //Roo.log('activite:'  + id);
46177         
46178         var tab = this.items[id];
46179         if(!tab){
46180             return null;
46181         }
46182         if(tab == this.active || tab.disabled){
46183             return tab;
46184         }
46185         var e = {};
46186         this.fireEvent("beforetabchange", this, e, tab);
46187         if(e.cancel !== true && !tab.disabled){
46188             if(this.active){
46189                 this.active.hide();
46190             }
46191             this.active = this.items[id];
46192             this.active.show();
46193             this.fireEvent("tabchange", this, this.active);
46194         }
46195         return tab;
46196     },
46197
46198     /**
46199      * Gets the active {@link Roo.TabPanelItem}.
46200      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46201      */
46202     getActiveTab : function(){
46203         return this.active;
46204     },
46205
46206     /**
46207      * Updates the tab body element to fit the height of the container element
46208      * for overflow scrolling
46209      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46210      */
46211     syncHeight : function(targetHeight){
46212         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46213         var bm = this.bodyEl.getMargins();
46214         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46215         this.bodyEl.setHeight(newHeight);
46216         return newHeight;
46217     },
46218
46219     onResize : function(){
46220         if(this.monitorResize){
46221             this.autoSizeTabs();
46222         }
46223     },
46224
46225     /**
46226      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46227      */
46228     beginUpdate : function(){
46229         this.updating = true;
46230     },
46231
46232     /**
46233      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46234      */
46235     endUpdate : function(){
46236         this.updating = false;
46237         this.autoSizeTabs();
46238     },
46239
46240     /**
46241      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46242      */
46243     autoSizeTabs : function()
46244     {
46245         var count = this.items.length;
46246         var vcount = count - this.hiddenCount;
46247         
46248         if (vcount < 2) {
46249             this.stripEl.hide();
46250         } else {
46251             this.stripEl.show();
46252         }
46253         
46254         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46255             return;
46256         }
46257         
46258         
46259         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46260         var availWidth = Math.floor(w / vcount);
46261         var b = this.stripBody;
46262         if(b.getWidth() > w){
46263             var tabs = this.items;
46264             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46265             if(availWidth < this.minTabWidth){
46266                 /*if(!this.sleft){    // incomplete scrolling code
46267                     this.createScrollButtons();
46268                 }
46269                 this.showScroll();
46270                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46271             }
46272         }else{
46273             if(this.currentTabWidth < this.preferredTabWidth){
46274                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46275             }
46276         }
46277     },
46278
46279     /**
46280      * Returns the number of tabs in this TabPanel.
46281      * @return {Number}
46282      */
46283      getCount : function(){
46284          return this.items.length;
46285      },
46286
46287     /**
46288      * Resizes all the tabs to the passed width
46289      * @param {Number} The new width
46290      */
46291     setTabWidth : function(width){
46292         this.currentTabWidth = width;
46293         for(var i = 0, len = this.items.length; i < len; i++) {
46294                 if(!this.items[i].isHidden()) {
46295                 this.items[i].setWidth(width);
46296             }
46297         }
46298     },
46299
46300     /**
46301      * Destroys this TabPanel
46302      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46303      */
46304     destroy : function(removeEl){
46305         Roo.EventManager.removeResizeListener(this.onResize, this);
46306         for(var i = 0, len = this.items.length; i < len; i++){
46307             this.items[i].purgeListeners();
46308         }
46309         if(removeEl === true){
46310             this.el.update("");
46311             this.el.remove();
46312         }
46313     },
46314     
46315     createStrip : function(container)
46316     {
46317         var strip = document.createElement("nav");
46318         strip.className = Roo.bootstrap.version == 4 ?
46319             "navbar-light bg-light" : 
46320             "navbar navbar-default"; //"x-tabs-wrap";
46321         container.appendChild(strip);
46322         return strip;
46323     },
46324     
46325     createStripList : function(strip)
46326     {
46327         // div wrapper for retard IE
46328         // returns the "tr" element.
46329         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46330         //'<div class="x-tabs-strip-wrap">'+
46331           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46332           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46333         return strip.firstChild; //.firstChild.firstChild.firstChild;
46334     },
46335     createBody : function(container)
46336     {
46337         var body = document.createElement("div");
46338         Roo.id(body, "tab-body");
46339         //Roo.fly(body).addClass("x-tabs-body");
46340         Roo.fly(body).addClass("tab-content");
46341         container.appendChild(body);
46342         return body;
46343     },
46344     createItemBody :function(bodyEl, id){
46345         var body = Roo.getDom(id);
46346         if(!body){
46347             body = document.createElement("div");
46348             body.id = id;
46349         }
46350         //Roo.fly(body).addClass("x-tabs-item-body");
46351         Roo.fly(body).addClass("tab-pane");
46352          bodyEl.insertBefore(body, bodyEl.firstChild);
46353         return body;
46354     },
46355     /** @private */
46356     createStripElements :  function(stripEl, text, closable, tpl)
46357     {
46358         var td = document.createElement("li"); // was td..
46359         td.className = 'nav-item';
46360         
46361         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46362         
46363         
46364         stripEl.appendChild(td);
46365         /*if(closable){
46366             td.className = "x-tabs-closable";
46367             if(!this.closeTpl){
46368                 this.closeTpl = new Roo.Template(
46369                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46370                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46371                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46372                 );
46373             }
46374             var el = this.closeTpl.overwrite(td, {"text": text});
46375             var close = el.getElementsByTagName("div")[0];
46376             var inner = el.getElementsByTagName("em")[0];
46377             return {"el": el, "close": close, "inner": inner};
46378         } else {
46379         */
46380         // not sure what this is..
46381 //            if(!this.tabTpl){
46382                 //this.tabTpl = new Roo.Template(
46383                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46384                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46385                 //);
46386 //                this.tabTpl = new Roo.Template(
46387 //                   '<a href="#">' +
46388 //                   '<span unselectable="on"' +
46389 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46390 //                            ' >{text}</span></a>'
46391 //                );
46392 //                
46393 //            }
46394
46395
46396             var template = tpl || this.tabTpl || false;
46397             
46398             if(!template){
46399                 template =  new Roo.Template(
46400                         Roo.bootstrap.version == 4 ? 
46401                             (
46402                                 '<a class="nav-link" href="#" unselectable="on"' +
46403                                      (this.disableTooltips ? '' : ' title="{text}"') +
46404                                      ' >{text}</a>'
46405                             ) : (
46406                                 '<a class="nav-link" href="#">' +
46407                                 '<span unselectable="on"' +
46408                                          (this.disableTooltips ? '' : ' title="{text}"') +
46409                                     ' >{text}</span></a>'
46410                             )
46411                 );
46412             }
46413             
46414             switch (typeof(template)) {
46415                 case 'object' :
46416                     break;
46417                 case 'string' :
46418                     template = new Roo.Template(template);
46419                     break;
46420                 default :
46421                     break;
46422             }
46423             
46424             var el = template.overwrite(td, {"text": text});
46425             
46426             var inner = el.getElementsByTagName("span")[0];
46427             
46428             return {"el": el, "inner": inner};
46429             
46430     }
46431         
46432     
46433 });
46434
46435 /**
46436  * @class Roo.TabPanelItem
46437  * @extends Roo.util.Observable
46438  * Represents an individual item (tab plus body) in a TabPanel.
46439  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46440  * @param {String} id The id of this TabPanelItem
46441  * @param {String} text The text for the tab of this TabPanelItem
46442  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46443  */
46444 Roo.bootstrap.panel.TabItem = function(config){
46445     /**
46446      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46447      * @type Roo.TabPanel
46448      */
46449     this.tabPanel = config.panel;
46450     /**
46451      * The id for this TabPanelItem
46452      * @type String
46453      */
46454     this.id = config.id;
46455     /** @private */
46456     this.disabled = false;
46457     /** @private */
46458     this.text = config.text;
46459     /** @private */
46460     this.loaded = false;
46461     this.closable = config.closable;
46462
46463     /**
46464      * The body element for this TabPanelItem.
46465      * @type Roo.Element
46466      */
46467     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46468     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46469     this.bodyEl.setStyle("display", "block");
46470     this.bodyEl.setStyle("zoom", "1");
46471     //this.hideAction();
46472
46473     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46474     /** @private */
46475     this.el = Roo.get(els.el);
46476     this.inner = Roo.get(els.inner, true);
46477      this.textEl = Roo.bootstrap.version == 4 ?
46478         this.el : Roo.get(this.el.dom.firstChild, true);
46479
46480     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46481     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46482
46483     
46484 //    this.el.on("mousedown", this.onTabMouseDown, this);
46485     this.el.on("click", this.onTabClick, this);
46486     /** @private */
46487     if(config.closable){
46488         var c = Roo.get(els.close, true);
46489         c.dom.title = this.closeText;
46490         c.addClassOnOver("close-over");
46491         c.on("click", this.closeClick, this);
46492      }
46493
46494     this.addEvents({
46495          /**
46496          * @event activate
46497          * Fires when this tab becomes the active tab.
46498          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46499          * @param {Roo.TabPanelItem} this
46500          */
46501         "activate": true,
46502         /**
46503          * @event beforeclose
46504          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46505          * @param {Roo.TabPanelItem} this
46506          * @param {Object} e Set cancel to true on this object to cancel the close.
46507          */
46508         "beforeclose": true,
46509         /**
46510          * @event close
46511          * Fires when this tab is closed.
46512          * @param {Roo.TabPanelItem} this
46513          */
46514          "close": true,
46515         /**
46516          * @event deactivate
46517          * Fires when this tab is no longer the active tab.
46518          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46519          * @param {Roo.TabPanelItem} this
46520          */
46521          "deactivate" : true
46522     });
46523     this.hidden = false;
46524
46525     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46526 };
46527
46528 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46529            {
46530     purgeListeners : function(){
46531        Roo.util.Observable.prototype.purgeListeners.call(this);
46532        this.el.removeAllListeners();
46533     },
46534     /**
46535      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46536      */
46537     show : function(){
46538         this.status_node.addClass("active");
46539         this.showAction();
46540         if(Roo.isOpera){
46541             this.tabPanel.stripWrap.repaint();
46542         }
46543         this.fireEvent("activate", this.tabPanel, this);
46544     },
46545
46546     /**
46547      * Returns true if this tab is the active tab.
46548      * @return {Boolean}
46549      */
46550     isActive : function(){
46551         return this.tabPanel.getActiveTab() == this;
46552     },
46553
46554     /**
46555      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46556      */
46557     hide : function(){
46558         this.status_node.removeClass("active");
46559         this.hideAction();
46560         this.fireEvent("deactivate", this.tabPanel, this);
46561     },
46562
46563     hideAction : function(){
46564         this.bodyEl.hide();
46565         this.bodyEl.setStyle("position", "absolute");
46566         this.bodyEl.setLeft("-20000px");
46567         this.bodyEl.setTop("-20000px");
46568     },
46569
46570     showAction : function(){
46571         this.bodyEl.setStyle("position", "relative");
46572         this.bodyEl.setTop("");
46573         this.bodyEl.setLeft("");
46574         this.bodyEl.show();
46575     },
46576
46577     /**
46578      * Set the tooltip for the tab.
46579      * @param {String} tooltip The tab's tooltip
46580      */
46581     setTooltip : function(text){
46582         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46583             this.textEl.dom.qtip = text;
46584             this.textEl.dom.removeAttribute('title');
46585         }else{
46586             this.textEl.dom.title = text;
46587         }
46588     },
46589
46590     onTabClick : function(e){
46591         e.preventDefault();
46592         this.tabPanel.activate(this.id);
46593     },
46594
46595     onTabMouseDown : function(e){
46596         e.preventDefault();
46597         this.tabPanel.activate(this.id);
46598     },
46599 /*
46600     getWidth : function(){
46601         return this.inner.getWidth();
46602     },
46603
46604     setWidth : function(width){
46605         var iwidth = width - this.linode.getPadding("lr");
46606         this.inner.setWidth(iwidth);
46607         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46608         this.linode.setWidth(width);
46609     },
46610 */
46611     /**
46612      * Show or hide the tab
46613      * @param {Boolean} hidden True to hide or false to show.
46614      */
46615     setHidden : function(hidden){
46616         this.hidden = hidden;
46617         this.linode.setStyle("display", hidden ? "none" : "");
46618     },
46619
46620     /**
46621      * Returns true if this tab is "hidden"
46622      * @return {Boolean}
46623      */
46624     isHidden : function(){
46625         return this.hidden;
46626     },
46627
46628     /**
46629      * Returns the text for this tab
46630      * @return {String}
46631      */
46632     getText : function(){
46633         return this.text;
46634     },
46635     /*
46636     autoSize : function(){
46637         //this.el.beginMeasure();
46638         this.textEl.setWidth(1);
46639         /*
46640          *  #2804 [new] Tabs in Roojs
46641          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46642          */
46643         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46644         //this.el.endMeasure();
46645     //},
46646
46647     /**
46648      * Sets the text for the tab (Note: this also sets the tooltip text)
46649      * @param {String} text The tab's text and tooltip
46650      */
46651     setText : function(text){
46652         this.text = text;
46653         this.textEl.update(text);
46654         this.setTooltip(text);
46655         //if(!this.tabPanel.resizeTabs){
46656         //    this.autoSize();
46657         //}
46658     },
46659     /**
46660      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46661      */
46662     activate : function(){
46663         this.tabPanel.activate(this.id);
46664     },
46665
46666     /**
46667      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46668      */
46669     disable : function(){
46670         if(this.tabPanel.active != this){
46671             this.disabled = true;
46672             this.status_node.addClass("disabled");
46673         }
46674     },
46675
46676     /**
46677      * Enables this TabPanelItem if it was previously disabled.
46678      */
46679     enable : function(){
46680         this.disabled = false;
46681         this.status_node.removeClass("disabled");
46682     },
46683
46684     /**
46685      * Sets the content for this TabPanelItem.
46686      * @param {String} content The content
46687      * @param {Boolean} loadScripts true to look for and load scripts
46688      */
46689     setContent : function(content, loadScripts){
46690         this.bodyEl.update(content, loadScripts);
46691     },
46692
46693     /**
46694      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46695      * @return {Roo.UpdateManager} The UpdateManager
46696      */
46697     getUpdateManager : function(){
46698         return this.bodyEl.getUpdateManager();
46699     },
46700
46701     /**
46702      * Set a URL to be used to load the content for this TabPanelItem.
46703      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46704      * @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)
46705      * @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)
46706      * @return {Roo.UpdateManager} The UpdateManager
46707      */
46708     setUrl : function(url, params, loadOnce){
46709         if(this.refreshDelegate){
46710             this.un('activate', this.refreshDelegate);
46711         }
46712         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46713         this.on("activate", this.refreshDelegate);
46714         return this.bodyEl.getUpdateManager();
46715     },
46716
46717     /** @private */
46718     _handleRefresh : function(url, params, loadOnce){
46719         if(!loadOnce || !this.loaded){
46720             var updater = this.bodyEl.getUpdateManager();
46721             updater.update(url, params, this._setLoaded.createDelegate(this));
46722         }
46723     },
46724
46725     /**
46726      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46727      *   Will fail silently if the setUrl method has not been called.
46728      *   This does not activate the panel, just updates its content.
46729      */
46730     refresh : function(){
46731         if(this.refreshDelegate){
46732            this.loaded = false;
46733            this.refreshDelegate();
46734         }
46735     },
46736
46737     /** @private */
46738     _setLoaded : function(){
46739         this.loaded = true;
46740     },
46741
46742     /** @private */
46743     closeClick : function(e){
46744         var o = {};
46745         e.stopEvent();
46746         this.fireEvent("beforeclose", this, o);
46747         if(o.cancel !== true){
46748             this.tabPanel.removeTab(this.id);
46749         }
46750     },
46751     /**
46752      * The text displayed in the tooltip for the close icon.
46753      * @type String
46754      */
46755     closeText : "Close this tab"
46756 });
46757 /**
46758 *    This script refer to:
46759 *    Title: International Telephone Input
46760 *    Author: Jack O'Connor
46761 *    Code version:  v12.1.12
46762 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46763 **/
46764
46765 Roo.bootstrap.form.PhoneInputData = function() {
46766     var d = [
46767       [
46768         "Afghanistan (‫افغانستان‬‎)",
46769         "af",
46770         "93"
46771       ],
46772       [
46773         "Albania (Shqipëri)",
46774         "al",
46775         "355"
46776       ],
46777       [
46778         "Algeria (‫الجزائر‬‎)",
46779         "dz",
46780         "213"
46781       ],
46782       [
46783         "American Samoa",
46784         "as",
46785         "1684"
46786       ],
46787       [
46788         "Andorra",
46789         "ad",
46790         "376"
46791       ],
46792       [
46793         "Angola",
46794         "ao",
46795         "244"
46796       ],
46797       [
46798         "Anguilla",
46799         "ai",
46800         "1264"
46801       ],
46802       [
46803         "Antigua and Barbuda",
46804         "ag",
46805         "1268"
46806       ],
46807       [
46808         "Argentina",
46809         "ar",
46810         "54"
46811       ],
46812       [
46813         "Armenia (Հայաստան)",
46814         "am",
46815         "374"
46816       ],
46817       [
46818         "Aruba",
46819         "aw",
46820         "297"
46821       ],
46822       [
46823         "Australia",
46824         "au",
46825         "61",
46826         0
46827       ],
46828       [
46829         "Austria (Österreich)",
46830         "at",
46831         "43"
46832       ],
46833       [
46834         "Azerbaijan (Azərbaycan)",
46835         "az",
46836         "994"
46837       ],
46838       [
46839         "Bahamas",
46840         "bs",
46841         "1242"
46842       ],
46843       [
46844         "Bahrain (‫البحرين‬‎)",
46845         "bh",
46846         "973"
46847       ],
46848       [
46849         "Bangladesh (বাংলাদেশ)",
46850         "bd",
46851         "880"
46852       ],
46853       [
46854         "Barbados",
46855         "bb",
46856         "1246"
46857       ],
46858       [
46859         "Belarus (Беларусь)",
46860         "by",
46861         "375"
46862       ],
46863       [
46864         "Belgium (België)",
46865         "be",
46866         "32"
46867       ],
46868       [
46869         "Belize",
46870         "bz",
46871         "501"
46872       ],
46873       [
46874         "Benin (Bénin)",
46875         "bj",
46876         "229"
46877       ],
46878       [
46879         "Bermuda",
46880         "bm",
46881         "1441"
46882       ],
46883       [
46884         "Bhutan (འབྲུག)",
46885         "bt",
46886         "975"
46887       ],
46888       [
46889         "Bolivia",
46890         "bo",
46891         "591"
46892       ],
46893       [
46894         "Bosnia and Herzegovina (Босна и Херцеговина)",
46895         "ba",
46896         "387"
46897       ],
46898       [
46899         "Botswana",
46900         "bw",
46901         "267"
46902       ],
46903       [
46904         "Brazil (Brasil)",
46905         "br",
46906         "55"
46907       ],
46908       [
46909         "British Indian Ocean Territory",
46910         "io",
46911         "246"
46912       ],
46913       [
46914         "British Virgin Islands",
46915         "vg",
46916         "1284"
46917       ],
46918       [
46919         "Brunei",
46920         "bn",
46921         "673"
46922       ],
46923       [
46924         "Bulgaria (България)",
46925         "bg",
46926         "359"
46927       ],
46928       [
46929         "Burkina Faso",
46930         "bf",
46931         "226"
46932       ],
46933       [
46934         "Burundi (Uburundi)",
46935         "bi",
46936         "257"
46937       ],
46938       [
46939         "Cambodia (កម្ពុជា)",
46940         "kh",
46941         "855"
46942       ],
46943       [
46944         "Cameroon (Cameroun)",
46945         "cm",
46946         "237"
46947       ],
46948       [
46949         "Canada",
46950         "ca",
46951         "1",
46952         1,
46953         ["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"]
46954       ],
46955       [
46956         "Cape Verde (Kabu Verdi)",
46957         "cv",
46958         "238"
46959       ],
46960       [
46961         "Caribbean Netherlands",
46962         "bq",
46963         "599",
46964         1
46965       ],
46966       [
46967         "Cayman Islands",
46968         "ky",
46969         "1345"
46970       ],
46971       [
46972         "Central African Republic (République centrafricaine)",
46973         "cf",
46974         "236"
46975       ],
46976       [
46977         "Chad (Tchad)",
46978         "td",
46979         "235"
46980       ],
46981       [
46982         "Chile",
46983         "cl",
46984         "56"
46985       ],
46986       [
46987         "China (中国)",
46988         "cn",
46989         "86"
46990       ],
46991       [
46992         "Christmas Island",
46993         "cx",
46994         "61",
46995         2
46996       ],
46997       [
46998         "Cocos (Keeling) Islands",
46999         "cc",
47000         "61",
47001         1
47002       ],
47003       [
47004         "Colombia",
47005         "co",
47006         "57"
47007       ],
47008       [
47009         "Comoros (‫جزر القمر‬‎)",
47010         "km",
47011         "269"
47012       ],
47013       [
47014         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47015         "cd",
47016         "243"
47017       ],
47018       [
47019         "Congo (Republic) (Congo-Brazzaville)",
47020         "cg",
47021         "242"
47022       ],
47023       [
47024         "Cook Islands",
47025         "ck",
47026         "682"
47027       ],
47028       [
47029         "Costa Rica",
47030         "cr",
47031         "506"
47032       ],
47033       [
47034         "Côte d’Ivoire",
47035         "ci",
47036         "225"
47037       ],
47038       [
47039         "Croatia (Hrvatska)",
47040         "hr",
47041         "385"
47042       ],
47043       [
47044         "Cuba",
47045         "cu",
47046         "53"
47047       ],
47048       [
47049         "Curaçao",
47050         "cw",
47051         "599",
47052         0
47053       ],
47054       [
47055         "Cyprus (Κύπρος)",
47056         "cy",
47057         "357"
47058       ],
47059       [
47060         "Czech Republic (Česká republika)",
47061         "cz",
47062         "420"
47063       ],
47064       [
47065         "Denmark (Danmark)",
47066         "dk",
47067         "45"
47068       ],
47069       [
47070         "Djibouti",
47071         "dj",
47072         "253"
47073       ],
47074       [
47075         "Dominica",
47076         "dm",
47077         "1767"
47078       ],
47079       [
47080         "Dominican Republic (República Dominicana)",
47081         "do",
47082         "1",
47083         2,
47084         ["809", "829", "849"]
47085       ],
47086       [
47087         "Ecuador",
47088         "ec",
47089         "593"
47090       ],
47091       [
47092         "Egypt (‫مصر‬‎)",
47093         "eg",
47094         "20"
47095       ],
47096       [
47097         "El Salvador",
47098         "sv",
47099         "503"
47100       ],
47101       [
47102         "Equatorial Guinea (Guinea Ecuatorial)",
47103         "gq",
47104         "240"
47105       ],
47106       [
47107         "Eritrea",
47108         "er",
47109         "291"
47110       ],
47111       [
47112         "Estonia (Eesti)",
47113         "ee",
47114         "372"
47115       ],
47116       [
47117         "Ethiopia",
47118         "et",
47119         "251"
47120       ],
47121       [
47122         "Falkland Islands (Islas Malvinas)",
47123         "fk",
47124         "500"
47125       ],
47126       [
47127         "Faroe Islands (Føroyar)",
47128         "fo",
47129         "298"
47130       ],
47131       [
47132         "Fiji",
47133         "fj",
47134         "679"
47135       ],
47136       [
47137         "Finland (Suomi)",
47138         "fi",
47139         "358",
47140         0
47141       ],
47142       [
47143         "France",
47144         "fr",
47145         "33"
47146       ],
47147       [
47148         "French Guiana (Guyane française)",
47149         "gf",
47150         "594"
47151       ],
47152       [
47153         "French Polynesia (Polynésie française)",
47154         "pf",
47155         "689"
47156       ],
47157       [
47158         "Gabon",
47159         "ga",
47160         "241"
47161       ],
47162       [
47163         "Gambia",
47164         "gm",
47165         "220"
47166       ],
47167       [
47168         "Georgia (საქართველო)",
47169         "ge",
47170         "995"
47171       ],
47172       [
47173         "Germany (Deutschland)",
47174         "de",
47175         "49"
47176       ],
47177       [
47178         "Ghana (Gaana)",
47179         "gh",
47180         "233"
47181       ],
47182       [
47183         "Gibraltar",
47184         "gi",
47185         "350"
47186       ],
47187       [
47188         "Greece (Ελλάδα)",
47189         "gr",
47190         "30"
47191       ],
47192       [
47193         "Greenland (Kalaallit Nunaat)",
47194         "gl",
47195         "299"
47196       ],
47197       [
47198         "Grenada",
47199         "gd",
47200         "1473"
47201       ],
47202       [
47203         "Guadeloupe",
47204         "gp",
47205         "590",
47206         0
47207       ],
47208       [
47209         "Guam",
47210         "gu",
47211         "1671"
47212       ],
47213       [
47214         "Guatemala",
47215         "gt",
47216         "502"
47217       ],
47218       [
47219         "Guernsey",
47220         "gg",
47221         "44",
47222         1
47223       ],
47224       [
47225         "Guinea (Guinée)",
47226         "gn",
47227         "224"
47228       ],
47229       [
47230         "Guinea-Bissau (Guiné Bissau)",
47231         "gw",
47232         "245"
47233       ],
47234       [
47235         "Guyana",
47236         "gy",
47237         "592"
47238       ],
47239       [
47240         "Haiti",
47241         "ht",
47242         "509"
47243       ],
47244       [
47245         "Honduras",
47246         "hn",
47247         "504"
47248       ],
47249       [
47250         "Hong Kong (香港)",
47251         "hk",
47252         "852"
47253       ],
47254       [
47255         "Hungary (Magyarország)",
47256         "hu",
47257         "36"
47258       ],
47259       [
47260         "Iceland (Ísland)",
47261         "is",
47262         "354"
47263       ],
47264       [
47265         "India (भारत)",
47266         "in",
47267         "91"
47268       ],
47269       [
47270         "Indonesia",
47271         "id",
47272         "62"
47273       ],
47274       [
47275         "Iran (‫ایران‬‎)",
47276         "ir",
47277         "98"
47278       ],
47279       [
47280         "Iraq (‫العراق‬‎)",
47281         "iq",
47282         "964"
47283       ],
47284       [
47285         "Ireland",
47286         "ie",
47287         "353"
47288       ],
47289       [
47290         "Isle of Man",
47291         "im",
47292         "44",
47293         2
47294       ],
47295       [
47296         "Israel (‫ישראל‬‎)",
47297         "il",
47298         "972"
47299       ],
47300       [
47301         "Italy (Italia)",
47302         "it",
47303         "39",
47304         0
47305       ],
47306       [
47307         "Jamaica",
47308         "jm",
47309         "1876"
47310       ],
47311       [
47312         "Japan (日本)",
47313         "jp",
47314         "81"
47315       ],
47316       [
47317         "Jersey",
47318         "je",
47319         "44",
47320         3
47321       ],
47322       [
47323         "Jordan (‫الأردن‬‎)",
47324         "jo",
47325         "962"
47326       ],
47327       [
47328         "Kazakhstan (Казахстан)",
47329         "kz",
47330         "7",
47331         1
47332       ],
47333       [
47334         "Kenya",
47335         "ke",
47336         "254"
47337       ],
47338       [
47339         "Kiribati",
47340         "ki",
47341         "686"
47342       ],
47343       [
47344         "Kosovo",
47345         "xk",
47346         "383"
47347       ],
47348       [
47349         "Kuwait (‫الكويت‬‎)",
47350         "kw",
47351         "965"
47352       ],
47353       [
47354         "Kyrgyzstan (Кыргызстан)",
47355         "kg",
47356         "996"
47357       ],
47358       [
47359         "Laos (ລາວ)",
47360         "la",
47361         "856"
47362       ],
47363       [
47364         "Latvia (Latvija)",
47365         "lv",
47366         "371"
47367       ],
47368       [
47369         "Lebanon (‫لبنان‬‎)",
47370         "lb",
47371         "961"
47372       ],
47373       [
47374         "Lesotho",
47375         "ls",
47376         "266"
47377       ],
47378       [
47379         "Liberia",
47380         "lr",
47381         "231"
47382       ],
47383       [
47384         "Libya (‫ليبيا‬‎)",
47385         "ly",
47386         "218"
47387       ],
47388       [
47389         "Liechtenstein",
47390         "li",
47391         "423"
47392       ],
47393       [
47394         "Lithuania (Lietuva)",
47395         "lt",
47396         "370"
47397       ],
47398       [
47399         "Luxembourg",
47400         "lu",
47401         "352"
47402       ],
47403       [
47404         "Macau (澳門)",
47405         "mo",
47406         "853"
47407       ],
47408       [
47409         "Macedonia (FYROM) (Македонија)",
47410         "mk",
47411         "389"
47412       ],
47413       [
47414         "Madagascar (Madagasikara)",
47415         "mg",
47416         "261"
47417       ],
47418       [
47419         "Malawi",
47420         "mw",
47421         "265"
47422       ],
47423       [
47424         "Malaysia",
47425         "my",
47426         "60"
47427       ],
47428       [
47429         "Maldives",
47430         "mv",
47431         "960"
47432       ],
47433       [
47434         "Mali",
47435         "ml",
47436         "223"
47437       ],
47438       [
47439         "Malta",
47440         "mt",
47441         "356"
47442       ],
47443       [
47444         "Marshall Islands",
47445         "mh",
47446         "692"
47447       ],
47448       [
47449         "Martinique",
47450         "mq",
47451         "596"
47452       ],
47453       [
47454         "Mauritania (‫موريتانيا‬‎)",
47455         "mr",
47456         "222"
47457       ],
47458       [
47459         "Mauritius (Moris)",
47460         "mu",
47461         "230"
47462       ],
47463       [
47464         "Mayotte",
47465         "yt",
47466         "262",
47467         1
47468       ],
47469       [
47470         "Mexico (México)",
47471         "mx",
47472         "52"
47473       ],
47474       [
47475         "Micronesia",
47476         "fm",
47477         "691"
47478       ],
47479       [
47480         "Moldova (Republica Moldova)",
47481         "md",
47482         "373"
47483       ],
47484       [
47485         "Monaco",
47486         "mc",
47487         "377"
47488       ],
47489       [
47490         "Mongolia (Монгол)",
47491         "mn",
47492         "976"
47493       ],
47494       [
47495         "Montenegro (Crna Gora)",
47496         "me",
47497         "382"
47498       ],
47499       [
47500         "Montserrat",
47501         "ms",
47502         "1664"
47503       ],
47504       [
47505         "Morocco (‫المغرب‬‎)",
47506         "ma",
47507         "212",
47508         0
47509       ],
47510       [
47511         "Mozambique (Moçambique)",
47512         "mz",
47513         "258"
47514       ],
47515       [
47516         "Myanmar (Burma) (မြန်မာ)",
47517         "mm",
47518         "95"
47519       ],
47520       [
47521         "Namibia (Namibië)",
47522         "na",
47523         "264"
47524       ],
47525       [
47526         "Nauru",
47527         "nr",
47528         "674"
47529       ],
47530       [
47531         "Nepal (नेपाल)",
47532         "np",
47533         "977"
47534       ],
47535       [
47536         "Netherlands (Nederland)",
47537         "nl",
47538         "31"
47539       ],
47540       [
47541         "New Caledonia (Nouvelle-Calédonie)",
47542         "nc",
47543         "687"
47544       ],
47545       [
47546         "New Zealand",
47547         "nz",
47548         "64"
47549       ],
47550       [
47551         "Nicaragua",
47552         "ni",
47553         "505"
47554       ],
47555       [
47556         "Niger (Nijar)",
47557         "ne",
47558         "227"
47559       ],
47560       [
47561         "Nigeria",
47562         "ng",
47563         "234"
47564       ],
47565       [
47566         "Niue",
47567         "nu",
47568         "683"
47569       ],
47570       [
47571         "Norfolk Island",
47572         "nf",
47573         "672"
47574       ],
47575       [
47576         "North Korea (조선 민주주의 인민 공화국)",
47577         "kp",
47578         "850"
47579       ],
47580       [
47581         "Northern Mariana Islands",
47582         "mp",
47583         "1670"
47584       ],
47585       [
47586         "Norway (Norge)",
47587         "no",
47588         "47",
47589         0
47590       ],
47591       [
47592         "Oman (‫عُمان‬‎)",
47593         "om",
47594         "968"
47595       ],
47596       [
47597         "Pakistan (‫پاکستان‬‎)",
47598         "pk",
47599         "92"
47600       ],
47601       [
47602         "Palau",
47603         "pw",
47604         "680"
47605       ],
47606       [
47607         "Palestine (‫فلسطين‬‎)",
47608         "ps",
47609         "970"
47610       ],
47611       [
47612         "Panama (Panamá)",
47613         "pa",
47614         "507"
47615       ],
47616       [
47617         "Papua New Guinea",
47618         "pg",
47619         "675"
47620       ],
47621       [
47622         "Paraguay",
47623         "py",
47624         "595"
47625       ],
47626       [
47627         "Peru (Perú)",
47628         "pe",
47629         "51"
47630       ],
47631       [
47632         "Philippines",
47633         "ph",
47634         "63"
47635       ],
47636       [
47637         "Poland (Polska)",
47638         "pl",
47639         "48"
47640       ],
47641       [
47642         "Portugal",
47643         "pt",
47644         "351"
47645       ],
47646       [
47647         "Puerto Rico",
47648         "pr",
47649         "1",
47650         3,
47651         ["787", "939"]
47652       ],
47653       [
47654         "Qatar (‫قطر‬‎)",
47655         "qa",
47656         "974"
47657       ],
47658       [
47659         "Réunion (La Réunion)",
47660         "re",
47661         "262",
47662         0
47663       ],
47664       [
47665         "Romania (România)",
47666         "ro",
47667         "40"
47668       ],
47669       [
47670         "Russia (Россия)",
47671         "ru",
47672         "7",
47673         0
47674       ],
47675       [
47676         "Rwanda",
47677         "rw",
47678         "250"
47679       ],
47680       [
47681         "Saint Barthélemy",
47682         "bl",
47683         "590",
47684         1
47685       ],
47686       [
47687         "Saint Helena",
47688         "sh",
47689         "290"
47690       ],
47691       [
47692         "Saint Kitts and Nevis",
47693         "kn",
47694         "1869"
47695       ],
47696       [
47697         "Saint Lucia",
47698         "lc",
47699         "1758"
47700       ],
47701       [
47702         "Saint Martin (Saint-Martin (partie française))",
47703         "mf",
47704         "590",
47705         2
47706       ],
47707       [
47708         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47709         "pm",
47710         "508"
47711       ],
47712       [
47713         "Saint Vincent and the Grenadines",
47714         "vc",
47715         "1784"
47716       ],
47717       [
47718         "Samoa",
47719         "ws",
47720         "685"
47721       ],
47722       [
47723         "San Marino",
47724         "sm",
47725         "378"
47726       ],
47727       [
47728         "São Tomé and Príncipe (São Tomé e Príncipe)",
47729         "st",
47730         "239"
47731       ],
47732       [
47733         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47734         "sa",
47735         "966"
47736       ],
47737       [
47738         "Senegal (Sénégal)",
47739         "sn",
47740         "221"
47741       ],
47742       [
47743         "Serbia (Србија)",
47744         "rs",
47745         "381"
47746       ],
47747       [
47748         "Seychelles",
47749         "sc",
47750         "248"
47751       ],
47752       [
47753         "Sierra Leone",
47754         "sl",
47755         "232"
47756       ],
47757       [
47758         "Singapore",
47759         "sg",
47760         "65"
47761       ],
47762       [
47763         "Sint Maarten",
47764         "sx",
47765         "1721"
47766       ],
47767       [
47768         "Slovakia (Slovensko)",
47769         "sk",
47770         "421"
47771       ],
47772       [
47773         "Slovenia (Slovenija)",
47774         "si",
47775         "386"
47776       ],
47777       [
47778         "Solomon Islands",
47779         "sb",
47780         "677"
47781       ],
47782       [
47783         "Somalia (Soomaaliya)",
47784         "so",
47785         "252"
47786       ],
47787       [
47788         "South Africa",
47789         "za",
47790         "27"
47791       ],
47792       [
47793         "South Korea (대한민국)",
47794         "kr",
47795         "82"
47796       ],
47797       [
47798         "South Sudan (‫جنوب السودان‬‎)",
47799         "ss",
47800         "211"
47801       ],
47802       [
47803         "Spain (España)",
47804         "es",
47805         "34"
47806       ],
47807       [
47808         "Sri Lanka (ශ්‍රී ලංකාව)",
47809         "lk",
47810         "94"
47811       ],
47812       [
47813         "Sudan (‫السودان‬‎)",
47814         "sd",
47815         "249"
47816       ],
47817       [
47818         "Suriname",
47819         "sr",
47820         "597"
47821       ],
47822       [
47823         "Svalbard and Jan Mayen",
47824         "sj",
47825         "47",
47826         1
47827       ],
47828       [
47829         "Swaziland",
47830         "sz",
47831         "268"
47832       ],
47833       [
47834         "Sweden (Sverige)",
47835         "se",
47836         "46"
47837       ],
47838       [
47839         "Switzerland (Schweiz)",
47840         "ch",
47841         "41"
47842       ],
47843       [
47844         "Syria (‫سوريا‬‎)",
47845         "sy",
47846         "963"
47847       ],
47848       [
47849         "Taiwan (台灣)",
47850         "tw",
47851         "886"
47852       ],
47853       [
47854         "Tajikistan",
47855         "tj",
47856         "992"
47857       ],
47858       [
47859         "Tanzania",
47860         "tz",
47861         "255"
47862       ],
47863       [
47864         "Thailand (ไทย)",
47865         "th",
47866         "66"
47867       ],
47868       [
47869         "Timor-Leste",
47870         "tl",
47871         "670"
47872       ],
47873       [
47874         "Togo",
47875         "tg",
47876         "228"
47877       ],
47878       [
47879         "Tokelau",
47880         "tk",
47881         "690"
47882       ],
47883       [
47884         "Tonga",
47885         "to",
47886         "676"
47887       ],
47888       [
47889         "Trinidad and Tobago",
47890         "tt",
47891         "1868"
47892       ],
47893       [
47894         "Tunisia (‫تونس‬‎)",
47895         "tn",
47896         "216"
47897       ],
47898       [
47899         "Turkey (Türkiye)",
47900         "tr",
47901         "90"
47902       ],
47903       [
47904         "Turkmenistan",
47905         "tm",
47906         "993"
47907       ],
47908       [
47909         "Turks and Caicos Islands",
47910         "tc",
47911         "1649"
47912       ],
47913       [
47914         "Tuvalu",
47915         "tv",
47916         "688"
47917       ],
47918       [
47919         "U.S. Virgin Islands",
47920         "vi",
47921         "1340"
47922       ],
47923       [
47924         "Uganda",
47925         "ug",
47926         "256"
47927       ],
47928       [
47929         "Ukraine (Україна)",
47930         "ua",
47931         "380"
47932       ],
47933       [
47934         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47935         "ae",
47936         "971"
47937       ],
47938       [
47939         "United Kingdom",
47940         "gb",
47941         "44",
47942         0
47943       ],
47944       [
47945         "United States",
47946         "us",
47947         "1",
47948         0
47949       ],
47950       [
47951         "Uruguay",
47952         "uy",
47953         "598"
47954       ],
47955       [
47956         "Uzbekistan (Oʻzbekiston)",
47957         "uz",
47958         "998"
47959       ],
47960       [
47961         "Vanuatu",
47962         "vu",
47963         "678"
47964       ],
47965       [
47966         "Vatican City (Città del Vaticano)",
47967         "va",
47968         "39",
47969         1
47970       ],
47971       [
47972         "Venezuela",
47973         "ve",
47974         "58"
47975       ],
47976       [
47977         "Vietnam (Việt Nam)",
47978         "vn",
47979         "84"
47980       ],
47981       [
47982         "Wallis and Futuna (Wallis-et-Futuna)",
47983         "wf",
47984         "681"
47985       ],
47986       [
47987         "Western Sahara (‫الصحراء الغربية‬‎)",
47988         "eh",
47989         "212",
47990         1
47991       ],
47992       [
47993         "Yemen (‫اليمن‬‎)",
47994         "ye",
47995         "967"
47996       ],
47997       [
47998         "Zambia",
47999         "zm",
48000         "260"
48001       ],
48002       [
48003         "Zimbabwe",
48004         "zw",
48005         "263"
48006       ],
48007       [
48008         "Åland Islands",
48009         "ax",
48010         "358",
48011         1
48012       ]
48013   ];
48014   
48015   return d;
48016 }/**
48017 *    This script refer to:
48018 *    Title: International Telephone Input
48019 *    Author: Jack O'Connor
48020 *    Code version:  v12.1.12
48021 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48022 **/
48023
48024 /**
48025  * @class Roo.bootstrap.form.PhoneInput
48026  * @extends Roo.bootstrap.form.TriggerField
48027  * An input with International dial-code selection
48028  
48029  * @cfg {String} defaultDialCode default '+852'
48030  * @cfg {Array} preferedCountries default []
48031   
48032  * @constructor
48033  * Create a new PhoneInput.
48034  * @param {Object} config Configuration options
48035  */
48036
48037 Roo.bootstrap.form.PhoneInput = function(config) {
48038     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48039 };
48040
48041 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48042         /**
48043         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48044         */
48045         listWidth: undefined,
48046         
48047         selectedClass: 'active',
48048         
48049         invalidClass : "has-warning",
48050         
48051         validClass: 'has-success',
48052         
48053         allowed: '0123456789',
48054         
48055         max_length: 15,
48056         
48057         /**
48058          * @cfg {String} defaultDialCode The default dial code when initializing the input
48059          */
48060         defaultDialCode: '+852',
48061         
48062         /**
48063          * @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
48064          */
48065         preferedCountries: false,
48066         
48067         getAutoCreate : function()
48068         {
48069             var data = Roo.bootstrap.form.PhoneInputData();
48070             var align = this.labelAlign || this.parentLabelAlign();
48071             var id = Roo.id();
48072             
48073             this.allCountries = [];
48074             this.dialCodeMapping = [];
48075             
48076             for (var i = 0; i < data.length; i++) {
48077               var c = data[i];
48078               this.allCountries[i] = {
48079                 name: c[0],
48080                 iso2: c[1],
48081                 dialCode: c[2],
48082                 priority: c[3] || 0,
48083                 areaCodes: c[4] || null
48084               };
48085               this.dialCodeMapping[c[2]] = {
48086                   name: c[0],
48087                   iso2: c[1],
48088                   priority: c[3] || 0,
48089                   areaCodes: c[4] || null
48090               };
48091             }
48092             
48093             var cfg = {
48094                 cls: 'form-group',
48095                 cn: []
48096             };
48097             
48098             var input =  {
48099                 tag: 'input',
48100                 id : id,
48101                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48102                 maxlength: this.max_length,
48103                 cls : 'form-control tel-input',
48104                 autocomplete: 'new-password'
48105             };
48106             
48107             var hiddenInput = {
48108                 tag: 'input',
48109                 type: 'hidden',
48110                 cls: 'hidden-tel-input'
48111             };
48112             
48113             if (this.name) {
48114                 hiddenInput.name = this.name;
48115             }
48116             
48117             if (this.disabled) {
48118                 input.disabled = true;
48119             }
48120             
48121             var flag_container = {
48122                 tag: 'div',
48123                 cls: 'flag-box',
48124                 cn: [
48125                     {
48126                         tag: 'div',
48127                         cls: 'flag'
48128                     },
48129                     {
48130                         tag: 'div',
48131                         cls: 'caret'
48132                     }
48133                 ]
48134             };
48135             
48136             var box = {
48137                 tag: 'div',
48138                 cls: this.hasFeedback ? 'has-feedback' : '',
48139                 cn: [
48140                     hiddenInput,
48141                     input,
48142                     {
48143                         tag: 'input',
48144                         cls: 'dial-code-holder',
48145                         disabled: true
48146                     }
48147                 ]
48148             };
48149             
48150             var container = {
48151                 cls: 'roo-select2-container input-group',
48152                 cn: [
48153                     flag_container,
48154                     box
48155                 ]
48156             };
48157             
48158             if (this.fieldLabel.length) {
48159                 var indicator = {
48160                     tag: 'i',
48161                     tooltip: 'This field is required'
48162                 };
48163                 
48164                 var label = {
48165                     tag: 'label',
48166                     'for':  id,
48167                     cls: 'control-label',
48168                     cn: []
48169                 };
48170                 
48171                 var label_text = {
48172                     tag: 'span',
48173                     html: this.fieldLabel
48174                 };
48175                 
48176                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48177                 label.cn = [
48178                     indicator,
48179                     label_text
48180                 ];
48181                 
48182                 if(this.indicatorpos == 'right') {
48183                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48184                     label.cn = [
48185                         label_text,
48186                         indicator
48187                     ];
48188                 }
48189                 
48190                 if(align == 'left') {
48191                     container = {
48192                         tag: 'div',
48193                         cn: [
48194                             container
48195                         ]
48196                     };
48197                     
48198                     if(this.labelWidth > 12){
48199                         label.style = "width: " + this.labelWidth + 'px';
48200                     }
48201                     if(this.labelWidth < 13 && this.labelmd == 0){
48202                         this.labelmd = this.labelWidth;
48203                     }
48204                     if(this.labellg > 0){
48205                         label.cls += ' col-lg-' + this.labellg;
48206                         input.cls += ' col-lg-' + (12 - this.labellg);
48207                     }
48208                     if(this.labelmd > 0){
48209                         label.cls += ' col-md-' + this.labelmd;
48210                         container.cls += ' col-md-' + (12 - this.labelmd);
48211                     }
48212                     if(this.labelsm > 0){
48213                         label.cls += ' col-sm-' + this.labelsm;
48214                         container.cls += ' col-sm-' + (12 - this.labelsm);
48215                     }
48216                     if(this.labelxs > 0){
48217                         label.cls += ' col-xs-' + this.labelxs;
48218                         container.cls += ' col-xs-' + (12 - this.labelxs);
48219                     }
48220                 }
48221             }
48222             
48223             cfg.cn = [
48224                 label,
48225                 container
48226             ];
48227             
48228             var settings = this;
48229             
48230             ['xs','sm','md','lg'].map(function(size){
48231                 if (settings[size]) {
48232                     cfg.cls += ' col-' + size + '-' + settings[size];
48233                 }
48234             });
48235             
48236             this.store = new Roo.data.Store({
48237                 proxy : new Roo.data.MemoryProxy({}),
48238                 reader : new Roo.data.JsonReader({
48239                     fields : [
48240                         {
48241                             'name' : 'name',
48242                             'type' : 'string'
48243                         },
48244                         {
48245                             'name' : 'iso2',
48246                             'type' : 'string'
48247                         },
48248                         {
48249                             'name' : 'dialCode',
48250                             'type' : 'string'
48251                         },
48252                         {
48253                             'name' : 'priority',
48254                             'type' : 'string'
48255                         },
48256                         {
48257                             'name' : 'areaCodes',
48258                             'type' : 'string'
48259                         }
48260                     ]
48261                 })
48262             });
48263             
48264             if(!this.preferedCountries) {
48265                 this.preferedCountries = [
48266                     'hk',
48267                     'gb',
48268                     'us'
48269                 ];
48270             }
48271             
48272             var p = this.preferedCountries.reverse();
48273             
48274             if(p) {
48275                 for (var i = 0; i < p.length; i++) {
48276                     for (var j = 0; j < this.allCountries.length; j++) {
48277                         if(this.allCountries[j].iso2 == p[i]) {
48278                             var t = this.allCountries[j];
48279                             this.allCountries.splice(j,1);
48280                             this.allCountries.unshift(t);
48281                         }
48282                     } 
48283                 }
48284             }
48285             
48286             this.store.proxy.data = {
48287                 success: true,
48288                 data: this.allCountries
48289             };
48290             
48291             return cfg;
48292         },
48293         
48294         initEvents : function()
48295         {
48296             this.createList();
48297             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48298             
48299             this.indicator = this.indicatorEl();
48300             this.flag = this.flagEl();
48301             this.dialCodeHolder = this.dialCodeHolderEl();
48302             
48303             this.trigger = this.el.select('div.flag-box',true).first();
48304             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48305             
48306             var _this = this;
48307             
48308             (function(){
48309                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48310                 _this.list.setWidth(lw);
48311             }).defer(100);
48312             
48313             this.list.on('mouseover', this.onViewOver, this);
48314             this.list.on('mousemove', this.onViewMove, this);
48315             this.inputEl().on("keyup", this.onKeyUp, this);
48316             this.inputEl().on("keypress", this.onKeyPress, this);
48317             
48318             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48319
48320             this.view = new Roo.View(this.list, this.tpl, {
48321                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48322             });
48323             
48324             this.view.on('click', this.onViewClick, this);
48325             this.setValue(this.defaultDialCode);
48326         },
48327         
48328         onTriggerClick : function(e)
48329         {
48330             Roo.log('trigger click');
48331             if(this.disabled){
48332                 return;
48333             }
48334             
48335             if(this.isExpanded()){
48336                 this.collapse();
48337                 this.hasFocus = false;
48338             }else {
48339                 this.store.load({});
48340                 this.hasFocus = true;
48341                 this.expand();
48342             }
48343         },
48344         
48345         isExpanded : function()
48346         {
48347             return this.list.isVisible();
48348         },
48349         
48350         collapse : function()
48351         {
48352             if(!this.isExpanded()){
48353                 return;
48354             }
48355             this.list.hide();
48356             Roo.get(document).un('mousedown', this.collapseIf, this);
48357             Roo.get(document).un('mousewheel', this.collapseIf, this);
48358             this.fireEvent('collapse', this);
48359             this.validate();
48360         },
48361         
48362         expand : function()
48363         {
48364             Roo.log('expand');
48365
48366             if(this.isExpanded() || !this.hasFocus){
48367                 return;
48368             }
48369             
48370             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48371             this.list.setWidth(lw);
48372             
48373             this.list.show();
48374             this.restrictHeight();
48375             
48376             Roo.get(document).on('mousedown', this.collapseIf, this);
48377             Roo.get(document).on('mousewheel', this.collapseIf, this);
48378             
48379             this.fireEvent('expand', this);
48380         },
48381         
48382         restrictHeight : function()
48383         {
48384             this.list.alignTo(this.inputEl(), this.listAlign);
48385             this.list.alignTo(this.inputEl(), this.listAlign);
48386         },
48387         
48388         onViewOver : function(e, t)
48389         {
48390             if(this.inKeyMode){
48391                 return;
48392             }
48393             var item = this.view.findItemFromChild(t);
48394             
48395             if(item){
48396                 var index = this.view.indexOf(item);
48397                 this.select(index, false);
48398             }
48399         },
48400
48401         // private
48402         onViewClick : function(view, doFocus, el, e)
48403         {
48404             var index = this.view.getSelectedIndexes()[0];
48405             
48406             var r = this.store.getAt(index);
48407             
48408             if(r){
48409                 this.onSelect(r, index);
48410             }
48411             if(doFocus !== false && !this.blockFocus){
48412                 this.inputEl().focus();
48413             }
48414         },
48415         
48416         onViewMove : function(e, t)
48417         {
48418             this.inKeyMode = false;
48419         },
48420         
48421         select : function(index, scrollIntoView)
48422         {
48423             this.selectedIndex = index;
48424             this.view.select(index);
48425             if(scrollIntoView !== false){
48426                 var el = this.view.getNode(index);
48427                 if(el){
48428                     this.list.scrollChildIntoView(el, false);
48429                 }
48430             }
48431         },
48432         
48433         createList : function()
48434         {
48435             this.list = Roo.get(document.body).createChild({
48436                 tag: 'ul',
48437                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48438                 style: 'display:none'
48439             });
48440             
48441             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48442         },
48443         
48444         collapseIf : function(e)
48445         {
48446             var in_combo  = e.within(this.el);
48447             var in_list =  e.within(this.list);
48448             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48449             
48450             if (in_combo || in_list || is_list) {
48451                 return;
48452             }
48453             this.collapse();
48454         },
48455         
48456         onSelect : function(record, index)
48457         {
48458             if(this.fireEvent('beforeselect', this, record, index) !== false){
48459                 
48460                 this.setFlagClass(record.data.iso2);
48461                 this.setDialCode(record.data.dialCode);
48462                 this.hasFocus = false;
48463                 this.collapse();
48464                 this.fireEvent('select', this, record, index);
48465             }
48466         },
48467         
48468         flagEl : function()
48469         {
48470             var flag = this.el.select('div.flag',true).first();
48471             if(!flag){
48472                 return false;
48473             }
48474             return flag;
48475         },
48476         
48477         dialCodeHolderEl : function()
48478         {
48479             var d = this.el.select('input.dial-code-holder',true).first();
48480             if(!d){
48481                 return false;
48482             }
48483             return d;
48484         },
48485         
48486         setDialCode : function(v)
48487         {
48488             this.dialCodeHolder.dom.value = '+'+v;
48489         },
48490         
48491         setFlagClass : function(n)
48492         {
48493             this.flag.dom.className = 'flag '+n;
48494         },
48495         
48496         getValue : function()
48497         {
48498             var v = this.inputEl().getValue();
48499             if(this.dialCodeHolder) {
48500                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48501             }
48502             return v;
48503         },
48504         
48505         setValue : function(v)
48506         {
48507             var d = this.getDialCode(v);
48508             
48509             //invalid dial code
48510             if(v.length == 0 || !d || d.length == 0) {
48511                 if(this.rendered){
48512                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48513                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48514                 }
48515                 return;
48516             }
48517             
48518             //valid dial code
48519             this.setFlagClass(this.dialCodeMapping[d].iso2);
48520             this.setDialCode(d);
48521             this.inputEl().dom.value = v.replace('+'+d,'');
48522             this.hiddenEl().dom.value = this.getValue();
48523             
48524             this.validate();
48525         },
48526         
48527         getDialCode : function(v)
48528         {
48529             v = v ||  '';
48530             
48531             if (v.length == 0) {
48532                 return this.dialCodeHolder.dom.value;
48533             }
48534             
48535             var dialCode = "";
48536             if (v.charAt(0) != "+") {
48537                 return false;
48538             }
48539             var numericChars = "";
48540             for (var i = 1; i < v.length; i++) {
48541               var c = v.charAt(i);
48542               if (!isNaN(c)) {
48543                 numericChars += c;
48544                 if (this.dialCodeMapping[numericChars]) {
48545                   dialCode = v.substr(1, i);
48546                 }
48547                 if (numericChars.length == 4) {
48548                   break;
48549                 }
48550               }
48551             }
48552             return dialCode;
48553         },
48554         
48555         reset : function()
48556         {
48557             this.setValue(this.defaultDialCode);
48558             this.validate();
48559         },
48560         
48561         hiddenEl : function()
48562         {
48563             return this.el.select('input.hidden-tel-input',true).first();
48564         },
48565         
48566         // after setting val
48567         onKeyUp : function(e){
48568             this.setValue(this.getValue());
48569         },
48570         
48571         onKeyPress : function(e){
48572             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48573                 e.stopEvent();
48574             }
48575         }
48576         
48577 });
48578 /**
48579  * @class Roo.bootstrap.form.MoneyField
48580  * @extends Roo.bootstrap.form.ComboBox
48581  * Bootstrap MoneyField class
48582  * 
48583  * @constructor
48584  * Create a new MoneyField.
48585  * @param {Object} config Configuration options
48586  */
48587
48588 Roo.bootstrap.form.MoneyField = function(config) {
48589     
48590     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48591     
48592 };
48593
48594 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48595     
48596     /**
48597      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48598      */
48599     allowDecimals : true,
48600     /**
48601      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48602      */
48603     decimalSeparator : ".",
48604     /**
48605      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48606      */
48607     decimalPrecision : 0,
48608     /**
48609      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48610      */
48611     allowNegative : true,
48612     /**
48613      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48614      */
48615     allowZero: true,
48616     /**
48617      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48618      */
48619     minValue : Number.NEGATIVE_INFINITY,
48620     /**
48621      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48622      */
48623     maxValue : Number.MAX_VALUE,
48624     /**
48625      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48626      */
48627     minText : "The minimum value for this field is {0}",
48628     /**
48629      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48630      */
48631     maxText : "The maximum value for this field is {0}",
48632     /**
48633      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48634      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48635      */
48636     nanText : "{0} is not a valid number",
48637     /**
48638      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48639      */
48640     castInt : true,
48641     /**
48642      * @cfg {String} defaults currency of the MoneyField
48643      * value should be in lkey
48644      */
48645     defaultCurrency : false,
48646     /**
48647      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48648      */
48649     thousandsDelimiter : false,
48650     /**
48651      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48652      */
48653     max_length: false,
48654     
48655     inputlg : 9,
48656     inputmd : 9,
48657     inputsm : 9,
48658     inputxs : 6,
48659      /**
48660      * @cfg {Roo.data.Store} store  Store to lookup currency??
48661      */
48662     store : false,
48663     
48664     getAutoCreate : function()
48665     {
48666         var align = this.labelAlign || this.parentLabelAlign();
48667         
48668         var id = Roo.id();
48669
48670         var cfg = {
48671             cls: 'form-group',
48672             cn: []
48673         };
48674
48675         var input =  {
48676             tag: 'input',
48677             id : id,
48678             cls : 'form-control roo-money-amount-input',
48679             autocomplete: 'new-password'
48680         };
48681         
48682         var hiddenInput = {
48683             tag: 'input',
48684             type: 'hidden',
48685             id: Roo.id(),
48686             cls: 'hidden-number-input'
48687         };
48688         
48689         if(this.max_length) {
48690             input.maxlength = this.max_length; 
48691         }
48692         
48693         if (this.name) {
48694             hiddenInput.name = this.name;
48695         }
48696
48697         if (this.disabled) {
48698             input.disabled = true;
48699         }
48700
48701         var clg = 12 - this.inputlg;
48702         var cmd = 12 - this.inputmd;
48703         var csm = 12 - this.inputsm;
48704         var cxs = 12 - this.inputxs;
48705         
48706         var container = {
48707             tag : 'div',
48708             cls : 'row roo-money-field',
48709             cn : [
48710                 {
48711                     tag : 'div',
48712                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48713                     cn : [
48714                         {
48715                             tag : 'div',
48716                             cls: 'roo-select2-container input-group',
48717                             cn: [
48718                                 {
48719                                     tag : 'input',
48720                                     cls : 'form-control roo-money-currency-input',
48721                                     autocomplete: 'new-password',
48722                                     readOnly : 1,
48723                                     name : this.currencyName
48724                                 },
48725                                 {
48726                                     tag :'span',
48727                                     cls : 'input-group-addon',
48728                                     cn : [
48729                                         {
48730                                             tag: 'span',
48731                                             cls: 'caret'
48732                                         }
48733                                     ]
48734                                 }
48735                             ]
48736                         }
48737                     ]
48738                 },
48739                 {
48740                     tag : 'div',
48741                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48742                     cn : [
48743                         {
48744                             tag: 'div',
48745                             cls: this.hasFeedback ? 'has-feedback' : '',
48746                             cn: [
48747                                 input
48748                             ]
48749                         }
48750                     ]
48751                 }
48752             ]
48753             
48754         };
48755         
48756         if (this.fieldLabel.length) {
48757             var indicator = {
48758                 tag: 'i',
48759                 tooltip: 'This field is required'
48760             };
48761
48762             var label = {
48763                 tag: 'label',
48764                 'for':  id,
48765                 cls: 'control-label',
48766                 cn: []
48767             };
48768
48769             var label_text = {
48770                 tag: 'span',
48771                 html: this.fieldLabel
48772             };
48773
48774             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48775             label.cn = [
48776                 indicator,
48777                 label_text
48778             ];
48779
48780             if(this.indicatorpos == 'right') {
48781                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48782                 label.cn = [
48783                     label_text,
48784                     indicator
48785                 ];
48786             }
48787
48788             if(align == 'left') {
48789                 container = {
48790                     tag: 'div',
48791                     cn: [
48792                         container
48793                     ]
48794                 };
48795
48796                 if(this.labelWidth > 12){
48797                     label.style = "width: " + this.labelWidth + 'px';
48798                 }
48799                 if(this.labelWidth < 13 && this.labelmd == 0){
48800                     this.labelmd = this.labelWidth;
48801                 }
48802                 if(this.labellg > 0){
48803                     label.cls += ' col-lg-' + this.labellg;
48804                     input.cls += ' col-lg-' + (12 - this.labellg);
48805                 }
48806                 if(this.labelmd > 0){
48807                     label.cls += ' col-md-' + this.labelmd;
48808                     container.cls += ' col-md-' + (12 - this.labelmd);
48809                 }
48810                 if(this.labelsm > 0){
48811                     label.cls += ' col-sm-' + this.labelsm;
48812                     container.cls += ' col-sm-' + (12 - this.labelsm);
48813                 }
48814                 if(this.labelxs > 0){
48815                     label.cls += ' col-xs-' + this.labelxs;
48816                     container.cls += ' col-xs-' + (12 - this.labelxs);
48817                 }
48818             }
48819         }
48820
48821         cfg.cn = [
48822             label,
48823             container,
48824             hiddenInput
48825         ];
48826         
48827         var settings = this;
48828
48829         ['xs','sm','md','lg'].map(function(size){
48830             if (settings[size]) {
48831                 cfg.cls += ' col-' + size + '-' + settings[size];
48832             }
48833         });
48834         
48835         return cfg;
48836     },
48837     
48838     initEvents : function()
48839     {
48840         this.indicator = this.indicatorEl();
48841         
48842         this.initCurrencyEvent();
48843         
48844         this.initNumberEvent();
48845     },
48846     
48847     initCurrencyEvent : function()
48848     {
48849         if (!this.store) {
48850             throw "can not find store for combo";
48851         }
48852         
48853         this.store = Roo.factory(this.store, Roo.data);
48854         this.store.parent = this;
48855         
48856         this.createList();
48857         
48858         this.triggerEl = this.el.select('.input-group-addon', true).first();
48859         
48860         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48861         
48862         var _this = this;
48863         
48864         (function(){
48865             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48866             _this.list.setWidth(lw);
48867         }).defer(100);
48868         
48869         this.list.on('mouseover', this.onViewOver, this);
48870         this.list.on('mousemove', this.onViewMove, this);
48871         this.list.on('scroll', this.onViewScroll, this);
48872         
48873         if(!this.tpl){
48874             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48875         }
48876         
48877         this.view = new Roo.View(this.list, this.tpl, {
48878             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48879         });
48880         
48881         this.view.on('click', this.onViewClick, this);
48882         
48883         this.store.on('beforeload', this.onBeforeLoad, this);
48884         this.store.on('load', this.onLoad, this);
48885         this.store.on('loadexception', this.onLoadException, this);
48886         
48887         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48888             "up" : function(e){
48889                 this.inKeyMode = true;
48890                 this.selectPrev();
48891             },
48892
48893             "down" : function(e){
48894                 if(!this.isExpanded()){
48895                     this.onTriggerClick();
48896                 }else{
48897                     this.inKeyMode = true;
48898                     this.selectNext();
48899                 }
48900             },
48901
48902             "enter" : function(e){
48903                 this.collapse();
48904                 
48905                 if(this.fireEvent("specialkey", this, e)){
48906                     this.onViewClick(false);
48907                 }
48908                 
48909                 return true;
48910             },
48911
48912             "esc" : function(e){
48913                 this.collapse();
48914             },
48915
48916             "tab" : function(e){
48917                 this.collapse();
48918                 
48919                 if(this.fireEvent("specialkey", this, e)){
48920                     this.onViewClick(false);
48921                 }
48922                 
48923                 return true;
48924             },
48925
48926             scope : this,
48927
48928             doRelay : function(foo, bar, hname){
48929                 if(hname == 'down' || this.scope.isExpanded()){
48930                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48931                 }
48932                 return true;
48933             },
48934
48935             forceKeyDown: true
48936         });
48937         
48938         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48939         
48940     },
48941     
48942     initNumberEvent : function(e)
48943     {
48944         this.inputEl().on("keydown" , this.fireKey,  this);
48945         this.inputEl().on("focus", this.onFocus,  this);
48946         this.inputEl().on("blur", this.onBlur,  this);
48947         
48948         this.inputEl().relayEvent('keyup', this);
48949         
48950         if(this.indicator){
48951             this.indicator.addClass('invisible');
48952         }
48953  
48954         this.originalValue = this.getValue();
48955         
48956         if(this.validationEvent == 'keyup'){
48957             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48958             this.inputEl().on('keyup', this.filterValidation, this);
48959         }
48960         else if(this.validationEvent !== false){
48961             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48962         }
48963         
48964         if(this.selectOnFocus){
48965             this.on("focus", this.preFocus, this);
48966             
48967         }
48968         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48969             this.inputEl().on("keypress", this.filterKeys, this);
48970         } else {
48971             this.inputEl().relayEvent('keypress', this);
48972         }
48973         
48974         var allowed = "0123456789";
48975         
48976         if(this.allowDecimals){
48977             allowed += this.decimalSeparator;
48978         }
48979         
48980         if(this.allowNegative){
48981             allowed += "-";
48982         }
48983         
48984         if(this.thousandsDelimiter) {
48985             allowed += ",";
48986         }
48987         
48988         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48989         
48990         var keyPress = function(e){
48991             
48992             var k = e.getKey();
48993             
48994             var c = e.getCharCode();
48995             
48996             if(
48997                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48998                     allowed.indexOf(String.fromCharCode(c)) === -1
48999             ){
49000                 e.stopEvent();
49001                 return;
49002             }
49003             
49004             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49005                 return;
49006             }
49007             
49008             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49009                 e.stopEvent();
49010             }
49011         };
49012         
49013         this.inputEl().on("keypress", keyPress, this);
49014         
49015     },
49016     
49017     onTriggerClick : function(e)
49018     {   
49019         if(this.disabled){
49020             return;
49021         }
49022         
49023         this.page = 0;
49024         this.loadNext = false;
49025         
49026         if(this.isExpanded()){
49027             this.collapse();
49028             return;
49029         }
49030         
49031         this.hasFocus = true;
49032         
49033         if(this.triggerAction == 'all') {
49034             this.doQuery(this.allQuery, true);
49035             return;
49036         }
49037         
49038         this.doQuery(this.getRawValue());
49039     },
49040     
49041     getCurrency : function()
49042     {   
49043         var v = this.currencyEl().getValue();
49044         
49045         return v;
49046     },
49047     
49048     restrictHeight : function()
49049     {
49050         this.list.alignTo(this.currencyEl(), this.listAlign);
49051         this.list.alignTo(this.currencyEl(), this.listAlign);
49052     },
49053     
49054     onViewClick : function(view, doFocus, el, e)
49055     {
49056         var index = this.view.getSelectedIndexes()[0];
49057         
49058         var r = this.store.getAt(index);
49059         
49060         if(r){
49061             this.onSelect(r, index);
49062         }
49063     },
49064     
49065     onSelect : function(record, index){
49066         
49067         if(this.fireEvent('beforeselect', this, record, index) !== false){
49068         
49069             this.setFromCurrencyData(index > -1 ? record.data : false);
49070             
49071             this.collapse();
49072             
49073             this.fireEvent('select', this, record, index);
49074         }
49075     },
49076     
49077     setFromCurrencyData : function(o)
49078     {
49079         var currency = '';
49080         
49081         this.lastCurrency = o;
49082         
49083         if (this.currencyField) {
49084             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49085         } else {
49086             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49087         }
49088         
49089         this.lastSelectionText = currency;
49090         
49091         //setting default currency
49092         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49093             this.setCurrency(this.defaultCurrency);
49094             return;
49095         }
49096         
49097         this.setCurrency(currency);
49098     },
49099     
49100     setFromData : function(o)
49101     {
49102         var c = {};
49103         
49104         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49105         
49106         this.setFromCurrencyData(c);
49107         
49108         var value = '';
49109         
49110         if (this.name) {
49111             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49112         } else {
49113             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49114         }
49115         
49116         this.setValue(value);
49117         
49118     },
49119     
49120     setCurrency : function(v)
49121     {   
49122         this.currencyValue = v;
49123         
49124         if(this.rendered){
49125             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49126             this.validate();
49127         }
49128     },
49129     
49130     setValue : function(v)
49131     {
49132         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49133         
49134         this.value = v;
49135         
49136         if(this.rendered){
49137             
49138             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49139             
49140             this.inputEl().dom.value = (v == '') ? '' :
49141                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49142             
49143             if(!this.allowZero && v === '0') {
49144                 this.hiddenEl().dom.value = '';
49145                 this.inputEl().dom.value = '';
49146             }
49147             
49148             this.validate();
49149         }
49150     },
49151     
49152     getRawValue : function()
49153     {
49154         var v = this.inputEl().getValue();
49155         
49156         return v;
49157     },
49158     
49159     getValue : function()
49160     {
49161         return this.fixPrecision(this.parseValue(this.getRawValue()));
49162     },
49163     
49164     parseValue : function(value)
49165     {
49166         if(this.thousandsDelimiter) {
49167             value += "";
49168             r = new RegExp(",", "g");
49169             value = value.replace(r, "");
49170         }
49171         
49172         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49173         return isNaN(value) ? '' : value;
49174         
49175     },
49176     
49177     fixPrecision : function(value)
49178     {
49179         if(this.thousandsDelimiter) {
49180             value += "";
49181             r = new RegExp(",", "g");
49182             value = value.replace(r, "");
49183         }
49184         
49185         var nan = isNaN(value);
49186         
49187         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49188             return nan ? '' : value;
49189         }
49190         return parseFloat(value).toFixed(this.decimalPrecision);
49191     },
49192     
49193     decimalPrecisionFcn : function(v)
49194     {
49195         return Math.floor(v);
49196     },
49197     
49198     validateValue : function(value)
49199     {
49200         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49201             return false;
49202         }
49203         
49204         var num = this.parseValue(value);
49205         
49206         if(isNaN(num)){
49207             this.markInvalid(String.format(this.nanText, value));
49208             return false;
49209         }
49210         
49211         if(num < this.minValue){
49212             this.markInvalid(String.format(this.minText, this.minValue));
49213             return false;
49214         }
49215         
49216         if(num > this.maxValue){
49217             this.markInvalid(String.format(this.maxText, this.maxValue));
49218             return false;
49219         }
49220         
49221         return true;
49222     },
49223     
49224     validate : function()
49225     {
49226         if(this.disabled || this.allowBlank){
49227             this.markValid();
49228             return true;
49229         }
49230         
49231         var currency = this.getCurrency();
49232         
49233         if(this.validateValue(this.getRawValue()) && currency.length){
49234             this.markValid();
49235             return true;
49236         }
49237         
49238         this.markInvalid();
49239         return false;
49240     },
49241     
49242     getName: function()
49243     {
49244         return this.name;
49245     },
49246     
49247     beforeBlur : function()
49248     {
49249         if(!this.castInt){
49250             return;
49251         }
49252         
49253         var v = this.parseValue(this.getRawValue());
49254         
49255         if(v || v == 0){
49256             this.setValue(v);
49257         }
49258     },
49259     
49260     onBlur : function()
49261     {
49262         this.beforeBlur();
49263         
49264         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49265             //this.el.removeClass(this.focusClass);
49266         }
49267         
49268         this.hasFocus = false;
49269         
49270         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49271             this.validate();
49272         }
49273         
49274         var v = this.getValue();
49275         
49276         if(String(v) !== String(this.startValue)){
49277             this.fireEvent('change', this, v, this.startValue);
49278         }
49279         
49280         this.fireEvent("blur", this);
49281     },
49282     
49283     inputEl : function()
49284     {
49285         return this.el.select('.roo-money-amount-input', true).first();
49286     },
49287     
49288     currencyEl : function()
49289     {
49290         return this.el.select('.roo-money-currency-input', true).first();
49291     },
49292     
49293     hiddenEl : function()
49294     {
49295         return this.el.select('input.hidden-number-input',true).first();
49296     }
49297     
49298 });/**
49299  * @class Roo.bootstrap.BezierSignature
49300  * @extends Roo.bootstrap.Component
49301  * Bootstrap BezierSignature class
49302  * This script refer to:
49303  *    Title: Signature Pad
49304  *    Author: szimek
49305  *    Availability: https://github.com/szimek/signature_pad
49306  *
49307  * @constructor
49308  * Create a new BezierSignature
49309  * @param {Object} config The config object
49310  */
49311
49312 Roo.bootstrap.BezierSignature = function(config){
49313     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49314     this.addEvents({
49315         "resize" : true
49316     });
49317 };
49318
49319 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49320 {
49321      
49322     curve_data: [],
49323     
49324     is_empty: true,
49325     
49326     mouse_btn_down: true,
49327     
49328     /**
49329      * @cfg {int} canvas height
49330      */
49331     canvas_height: '200px',
49332     
49333     /**
49334      * @cfg {float|function} Radius of a single dot.
49335      */ 
49336     dot_size: false,
49337     
49338     /**
49339      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49340      */
49341     min_width: 0.5,
49342     
49343     /**
49344      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49345      */
49346     max_width: 2.5,
49347     
49348     /**
49349      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49350      */
49351     throttle: 16,
49352     
49353     /**
49354      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49355      */
49356     min_distance: 5,
49357     
49358     /**
49359      * @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.
49360      */
49361     bg_color: 'rgba(0, 0, 0, 0)',
49362     
49363     /**
49364      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49365      */
49366     dot_color: 'black',
49367     
49368     /**
49369      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49370      */ 
49371     velocity_filter_weight: 0.7,
49372     
49373     /**
49374      * @cfg {function} Callback when stroke begin. 
49375      */
49376     onBegin: false,
49377     
49378     /**
49379      * @cfg {function} Callback when stroke end.
49380      */
49381     onEnd: false,
49382     
49383     getAutoCreate : function()
49384     {
49385         var cls = 'roo-signature column';
49386         
49387         if(this.cls){
49388             cls += ' ' + this.cls;
49389         }
49390         
49391         var col_sizes = [
49392             'lg',
49393             'md',
49394             'sm',
49395             'xs'
49396         ];
49397         
49398         for(var i = 0; i < col_sizes.length; i++) {
49399             if(this[col_sizes[i]]) {
49400                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49401             }
49402         }
49403         
49404         var cfg = {
49405             tag: 'div',
49406             cls: cls,
49407             cn: [
49408                 {
49409                     tag: 'div',
49410                     cls: 'roo-signature-body',
49411                     cn: [
49412                         {
49413                             tag: 'canvas',
49414                             cls: 'roo-signature-body-canvas',
49415                             height: this.canvas_height,
49416                             width: this.canvas_width
49417                         }
49418                     ]
49419                 },
49420                 {
49421                     tag: 'input',
49422                     type: 'file',
49423                     style: 'display: none'
49424                 }
49425             ]
49426         };
49427         
49428         return cfg;
49429     },
49430     
49431     initEvents: function() 
49432     {
49433         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49434         
49435         var canvas = this.canvasEl();
49436         
49437         // mouse && touch event swapping...
49438         canvas.dom.style.touchAction = 'none';
49439         canvas.dom.style.msTouchAction = 'none';
49440         
49441         this.mouse_btn_down = false;
49442         canvas.on('mousedown', this._handleMouseDown, this);
49443         canvas.on('mousemove', this._handleMouseMove, this);
49444         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49445         
49446         if (window.PointerEvent) {
49447             canvas.on('pointerdown', this._handleMouseDown, this);
49448             canvas.on('pointermove', this._handleMouseMove, this);
49449             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49450         }
49451         
49452         if ('ontouchstart' in window) {
49453             canvas.on('touchstart', this._handleTouchStart, this);
49454             canvas.on('touchmove', this._handleTouchMove, this);
49455             canvas.on('touchend', this._handleTouchEnd, this);
49456         }
49457         
49458         Roo.EventManager.onWindowResize(this.resize, this, true);
49459         
49460         // file input event
49461         this.fileEl().on('change', this.uploadImage, this);
49462         
49463         this.clear();
49464         
49465         this.resize();
49466     },
49467     
49468     resize: function(){
49469         
49470         var canvas = this.canvasEl().dom;
49471         var ctx = this.canvasElCtx();
49472         var img_data = false;
49473         
49474         if(canvas.width > 0) {
49475             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49476         }
49477         // setting canvas width will clean img data
49478         canvas.width = 0;
49479         
49480         var style = window.getComputedStyle ? 
49481             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49482             
49483         var padding_left = parseInt(style.paddingLeft) || 0;
49484         var padding_right = parseInt(style.paddingRight) || 0;
49485         
49486         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49487         
49488         if(img_data) {
49489             ctx.putImageData(img_data, 0, 0);
49490         }
49491     },
49492     
49493     _handleMouseDown: function(e)
49494     {
49495         if (e.browserEvent.which === 1) {
49496             this.mouse_btn_down = true;
49497             this.strokeBegin(e);
49498         }
49499     },
49500     
49501     _handleMouseMove: function (e)
49502     {
49503         if (this.mouse_btn_down) {
49504             this.strokeMoveUpdate(e);
49505         }
49506     },
49507     
49508     _handleMouseUp: function (e)
49509     {
49510         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49511             this.mouse_btn_down = false;
49512             this.strokeEnd(e);
49513         }
49514     },
49515     
49516     _handleTouchStart: function (e) {
49517         
49518         e.preventDefault();
49519         if (e.browserEvent.targetTouches.length === 1) {
49520             // var touch = e.browserEvent.changedTouches[0];
49521             // this.strokeBegin(touch);
49522             
49523              this.strokeBegin(e); // assume e catching the correct xy...
49524         }
49525     },
49526     
49527     _handleTouchMove: function (e) {
49528         e.preventDefault();
49529         // var touch = event.targetTouches[0];
49530         // _this._strokeMoveUpdate(touch);
49531         this.strokeMoveUpdate(e);
49532     },
49533     
49534     _handleTouchEnd: function (e) {
49535         var wasCanvasTouched = e.target === this.canvasEl().dom;
49536         if (wasCanvasTouched) {
49537             e.preventDefault();
49538             // var touch = event.changedTouches[0];
49539             // _this._strokeEnd(touch);
49540             this.strokeEnd(e);
49541         }
49542     },
49543     
49544     reset: function () {
49545         this._lastPoints = [];
49546         this._lastVelocity = 0;
49547         this._lastWidth = (this.min_width + this.max_width) / 2;
49548         this.canvasElCtx().fillStyle = this.dot_color;
49549     },
49550     
49551     strokeMoveUpdate: function(e)
49552     {
49553         this.strokeUpdate(e);
49554         
49555         if (this.throttle) {
49556             this.throttleStroke(this.strokeUpdate, this.throttle);
49557         }
49558         else {
49559             this.strokeUpdate(e);
49560         }
49561     },
49562     
49563     strokeBegin: function(e)
49564     {
49565         var newPointGroup = {
49566             color: this.dot_color,
49567             points: []
49568         };
49569         
49570         if (typeof this.onBegin === 'function') {
49571             this.onBegin(e);
49572         }
49573         
49574         this.curve_data.push(newPointGroup);
49575         this.reset();
49576         this.strokeUpdate(e);
49577     },
49578     
49579     strokeUpdate: function(e)
49580     {
49581         var rect = this.canvasEl().dom.getBoundingClientRect();
49582         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49583         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49584         var lastPoints = lastPointGroup.points;
49585         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49586         var isLastPointTooClose = lastPoint
49587             ? point.distanceTo(lastPoint) <= this.min_distance
49588             : false;
49589         var color = lastPointGroup.color;
49590         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49591             var curve = this.addPoint(point);
49592             if (!lastPoint) {
49593                 this.drawDot({color: color, point: point});
49594             }
49595             else if (curve) {
49596                 this.drawCurve({color: color, curve: curve});
49597             }
49598             lastPoints.push({
49599                 time: point.time,
49600                 x: point.x,
49601                 y: point.y
49602             });
49603         }
49604     },
49605     
49606     strokeEnd: function(e)
49607     {
49608         this.strokeUpdate(e);
49609         if (typeof this.onEnd === 'function') {
49610             this.onEnd(e);
49611         }
49612     },
49613     
49614     addPoint:  function (point) {
49615         var _lastPoints = this._lastPoints;
49616         _lastPoints.push(point);
49617         if (_lastPoints.length > 2) {
49618             if (_lastPoints.length === 3) {
49619                 _lastPoints.unshift(_lastPoints[0]);
49620             }
49621             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49622             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49623             _lastPoints.shift();
49624             return curve;
49625         }
49626         return null;
49627     },
49628     
49629     calculateCurveWidths: function (startPoint, endPoint) {
49630         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49631             (1 - this.velocity_filter_weight) * this._lastVelocity;
49632
49633         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49634         var widths = {
49635             end: newWidth,
49636             start: this._lastWidth
49637         };
49638         
49639         this._lastVelocity = velocity;
49640         this._lastWidth = newWidth;
49641         return widths;
49642     },
49643     
49644     drawDot: function (_a) {
49645         var color = _a.color, point = _a.point;
49646         var ctx = this.canvasElCtx();
49647         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49648         ctx.beginPath();
49649         this.drawCurveSegment(point.x, point.y, width);
49650         ctx.closePath();
49651         ctx.fillStyle = color;
49652         ctx.fill();
49653     },
49654     
49655     drawCurve: function (_a) {
49656         var color = _a.color, curve = _a.curve;
49657         var ctx = this.canvasElCtx();
49658         var widthDelta = curve.endWidth - curve.startWidth;
49659         var drawSteps = Math.floor(curve.length()) * 2;
49660         ctx.beginPath();
49661         ctx.fillStyle = color;
49662         for (var i = 0; i < drawSteps; i += 1) {
49663         var t = i / drawSteps;
49664         var tt = t * t;
49665         var ttt = tt * t;
49666         var u = 1 - t;
49667         var uu = u * u;
49668         var uuu = uu * u;
49669         var x = uuu * curve.startPoint.x;
49670         x += 3 * uu * t * curve.control1.x;
49671         x += 3 * u * tt * curve.control2.x;
49672         x += ttt * curve.endPoint.x;
49673         var y = uuu * curve.startPoint.y;
49674         y += 3 * uu * t * curve.control1.y;
49675         y += 3 * u * tt * curve.control2.y;
49676         y += ttt * curve.endPoint.y;
49677         var width = curve.startWidth + ttt * widthDelta;
49678         this.drawCurveSegment(x, y, width);
49679         }
49680         ctx.closePath();
49681         ctx.fill();
49682     },
49683     
49684     drawCurveSegment: function (x, y, width) {
49685         var ctx = this.canvasElCtx();
49686         ctx.moveTo(x, y);
49687         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49688         this.is_empty = false;
49689     },
49690     
49691     clear: function()
49692     {
49693         var ctx = this.canvasElCtx();
49694         var canvas = this.canvasEl().dom;
49695         ctx.fillStyle = this.bg_color;
49696         ctx.clearRect(0, 0, canvas.width, canvas.height);
49697         ctx.fillRect(0, 0, canvas.width, canvas.height);
49698         this.curve_data = [];
49699         this.reset();
49700         this.is_empty = true;
49701     },
49702     
49703     fileEl: function()
49704     {
49705         return  this.el.select('input',true).first();
49706     },
49707     
49708     canvasEl: function()
49709     {
49710         return this.el.select('canvas',true).first();
49711     },
49712     
49713     canvasElCtx: function()
49714     {
49715         return this.el.select('canvas',true).first().dom.getContext('2d');
49716     },
49717     
49718     getImage: function(type)
49719     {
49720         if(this.is_empty) {
49721             return false;
49722         }
49723         
49724         // encryption ?
49725         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49726     },
49727     
49728     drawFromImage: function(img_src)
49729     {
49730         var img = new Image();
49731         
49732         img.onload = function(){
49733             this.canvasElCtx().drawImage(img, 0, 0);
49734         }.bind(this);
49735         
49736         img.src = img_src;
49737         
49738         this.is_empty = false;
49739     },
49740     
49741     selectImage: function()
49742     {
49743         this.fileEl().dom.click();
49744     },
49745     
49746     uploadImage: function(e)
49747     {
49748         var reader = new FileReader();
49749         
49750         reader.onload = function(e){
49751             var img = new Image();
49752             img.onload = function(){
49753                 this.reset();
49754                 this.canvasElCtx().drawImage(img, 0, 0);
49755             }.bind(this);
49756             img.src = e.target.result;
49757         }.bind(this);
49758         
49759         reader.readAsDataURL(e.target.files[0]);
49760     },
49761     
49762     // Bezier Point Constructor
49763     Point: (function () {
49764         function Point(x, y, time) {
49765             this.x = x;
49766             this.y = y;
49767             this.time = time || Date.now();
49768         }
49769         Point.prototype.distanceTo = function (start) {
49770             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49771         };
49772         Point.prototype.equals = function (other) {
49773             return this.x === other.x && this.y === other.y && this.time === other.time;
49774         };
49775         Point.prototype.velocityFrom = function (start) {
49776             return this.time !== start.time
49777             ? this.distanceTo(start) / (this.time - start.time)
49778             : 0;
49779         };
49780         return Point;
49781     }()),
49782     
49783     
49784     // Bezier Constructor
49785     Bezier: (function () {
49786         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49787             this.startPoint = startPoint;
49788             this.control2 = control2;
49789             this.control1 = control1;
49790             this.endPoint = endPoint;
49791             this.startWidth = startWidth;
49792             this.endWidth = endWidth;
49793         }
49794         Bezier.fromPoints = function (points, widths, scope) {
49795             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49796             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49797             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49798         };
49799         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49800             var dx1 = s1.x - s2.x;
49801             var dy1 = s1.y - s2.y;
49802             var dx2 = s2.x - s3.x;
49803             var dy2 = s2.y - s3.y;
49804             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49805             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49806             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49807             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49808             var dxm = m1.x - m2.x;
49809             var dym = m1.y - m2.y;
49810             var k = l2 / (l1 + l2);
49811             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49812             var tx = s2.x - cm.x;
49813             var ty = s2.y - cm.y;
49814             return {
49815                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49816                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49817             };
49818         };
49819         Bezier.prototype.length = function () {
49820             var steps = 10;
49821             var length = 0;
49822             var px;
49823             var py;
49824             for (var i = 0; i <= steps; i += 1) {
49825                 var t = i / steps;
49826                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49827                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49828                 if (i > 0) {
49829                     var xdiff = cx - px;
49830                     var ydiff = cy - py;
49831                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49832                 }
49833                 px = cx;
49834                 py = cy;
49835             }
49836             return length;
49837         };
49838         Bezier.prototype.point = function (t, start, c1, c2, end) {
49839             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49840             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49841             + (3.0 * c2 * (1.0 - t) * t * t)
49842             + (end * t * t * t);
49843         };
49844         return Bezier;
49845     }()),
49846     
49847     throttleStroke: function(fn, wait) {
49848       if (wait === void 0) { wait = 250; }
49849       var previous = 0;
49850       var timeout = null;
49851       var result;
49852       var storedContext;
49853       var storedArgs;
49854       var later = function () {
49855           previous = Date.now();
49856           timeout = null;
49857           result = fn.apply(storedContext, storedArgs);
49858           if (!timeout) {
49859               storedContext = null;
49860               storedArgs = [];
49861           }
49862       };
49863       return function wrapper() {
49864           var args = [];
49865           for (var _i = 0; _i < arguments.length; _i++) {
49866               args[_i] = arguments[_i];
49867           }
49868           var now = Date.now();
49869           var remaining = wait - (now - previous);
49870           storedContext = this;
49871           storedArgs = args;
49872           if (remaining <= 0 || remaining > wait) {
49873               if (timeout) {
49874                   clearTimeout(timeout);
49875                   timeout = null;
49876               }
49877               previous = now;
49878               result = fn.apply(storedContext, storedArgs);
49879               if (!timeout) {
49880                   storedContext = null;
49881                   storedArgs = [];
49882               }
49883           }
49884           else if (!timeout) {
49885               timeout = window.setTimeout(later, remaining);
49886           }
49887           return result;
49888       };
49889   }
49890   
49891 });
49892
49893  
49894
49895  // old names for form elements
49896 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49897 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49898 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49899 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49900 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49901 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49902 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49903 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49904 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49905 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49906 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49907 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49908 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49909 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49910 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49911 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49912 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49913 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49914 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49915 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49916 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49917 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49918 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49919 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49920 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49921 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49922
49923 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49924 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49925
49926 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49927 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49928
49929 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49930 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49931 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49932 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49933