merge label code in text area and input
[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     
4549     closeClick : function()
4550     {
4551         this.hide();
4552     },
4553     
4554     initEvents : function()
4555     {
4556         if (this.allow_close) {
4557             this.closeEl.on('click', this.closeClick, this);
4558         }
4559         Roo.EventManager.onWindowResize(this.resize, this, true);
4560         if (this.editableTitle) {
4561             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4562             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4563             this.headerEditEl.on('keyup', function(e) {
4564                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4565                         this.toggleHeaderInput(false)
4566                     }
4567                 }, this);
4568             this.headerEditEl.on('blur', function(e) {
4569                 this.toggleHeaderInput(false)
4570             },this);
4571         }
4572
4573     },
4574   
4575
4576     resize : function()
4577     {
4578         this.maskEl.setSize(
4579             Roo.lib.Dom.getViewWidth(true),
4580             Roo.lib.Dom.getViewHeight(true)
4581         );
4582         
4583         if (this.fitwindow) {
4584             
4585            this.dialogEl.setStyle( { 'max-width' : '100%' });
4586             this.setSize(
4587                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4588                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4589             );
4590             return;
4591         }
4592         
4593         if(this.max_width !== 0) {
4594             
4595             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4596             
4597             if(this.height) {
4598                 this.setSize(w, this.height);
4599                 return;
4600             }
4601             
4602             if(this.max_height) {
4603                 this.setSize(w,Math.min(
4604                     this.max_height,
4605                     Roo.lib.Dom.getViewportHeight(true) - 60
4606                 ));
4607                 
4608                 return;
4609             }
4610             
4611             if(!this.fit_content) {
4612                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4613                 return;
4614             }
4615             
4616             this.setSize(w, Math.min(
4617                 60 +
4618                 this.headerEl.getHeight() + 
4619                 this.footerEl.getHeight() + 
4620                 this.getChildHeight(this.bodyEl.dom.childNodes),
4621                 Roo.lib.Dom.getViewportHeight(true) - 60)
4622             );
4623         }
4624         
4625     },
4626
4627     setSize : function(w,h)
4628     {
4629         if (!w && !h) {
4630             return;
4631         }
4632         
4633         this.resizeTo(w,h);
4634         // any layout/border etc.. resize..
4635         (function () {
4636             this.items.forEach( function(e) {
4637                 e.layout ? e.layout() : false;
4638
4639             });
4640         }).defer(100,this);
4641         
4642     },
4643
4644     show : function() {
4645
4646         if (!this.rendered) {
4647             this.render();
4648         }
4649         this.toggleHeaderInput(false);
4650         //this.el.setStyle('display', 'block');
4651         this.el.removeClass('hideing');
4652         this.el.dom.style.display='block';
4653         
4654         Roo.get(document.body).addClass('modal-open');
4655  
4656         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4657             
4658             (function(){
4659                 this.el.addClass('show');
4660                 this.el.addClass('in');
4661             }).defer(50, this);
4662         }else{
4663             this.el.addClass('show');
4664             this.el.addClass('in');
4665         }
4666
4667         // not sure how we can show data in here..
4668         //if (this.tmpl) {
4669         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4670         //}
4671
4672         Roo.get(document.body).addClass("x-body-masked");
4673         
4674         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4675         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4676         this.maskEl.dom.style.display = 'block';
4677         this.maskEl.addClass('show');
4678         
4679         
4680         this.resize();
4681         
4682         this.fireEvent('show', this);
4683
4684         // set zindex here - otherwise it appears to be ignored...
4685         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4686         
4687         
4688         // this is for children that are... layout.Border 
4689         (function () {
4690             this.items.forEach( function(e) {
4691                 e.layout ? e.layout() : false;
4692
4693             });
4694         }).defer(100,this);
4695
4696     },
4697     hide : function()
4698     {
4699         if(this.fireEvent("beforehide", this) !== false){
4700             
4701             this.maskEl.removeClass('show');
4702             
4703             this.maskEl.dom.style.display = '';
4704             Roo.get(document.body).removeClass("x-body-masked");
4705             this.el.removeClass('in');
4706             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4707
4708             if(this.animate){ // why
4709                 this.el.addClass('hideing');
4710                 this.el.removeClass('show');
4711                 (function(){
4712                     if (!this.el.hasClass('hideing')) {
4713                         return; // it's been shown again...
4714                     }
4715                     
4716                     this.el.dom.style.display='';
4717
4718                     Roo.get(document.body).removeClass('modal-open');
4719                     this.el.removeClass('hideing');
4720                 }).defer(150,this);
4721                 
4722             }else{
4723                 this.el.removeClass('show');
4724                 this.el.dom.style.display='';
4725                 Roo.get(document.body).removeClass('modal-open');
4726
4727             }
4728             this.fireEvent('hide', this);
4729         }
4730     },
4731     isVisible : function()
4732     {
4733         
4734         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4735         
4736     },
4737
4738     addButton : function(str, cb)
4739     {
4740
4741
4742         var b = Roo.apply({}, { html : str } );
4743         b.xns = b.xns || Roo.bootstrap;
4744         b.xtype = b.xtype || 'Button';
4745         if (typeof(b.listeners) == 'undefined') {
4746             b.listeners = { click : cb.createDelegate(this)  };
4747         }
4748
4749         var btn = Roo.factory(b);
4750
4751         btn.render(this.getButtonContainer());
4752
4753         return btn;
4754
4755     },
4756
4757     setDefaultButton : function(btn)
4758     {
4759         //this.el.select('.modal-footer').()
4760     },
4761
4762     resizeTo: function(w,h)
4763     {
4764         this.dialogEl.setWidth(w);
4765         
4766         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4767
4768         this.bodyEl.setHeight(h - diff);
4769         
4770         this.fireEvent('resize', this);
4771     },
4772     
4773     setContentSize  : function(w, h)
4774     {
4775
4776     },
4777     onButtonClick: function(btn,e)
4778     {
4779         //Roo.log([a,b,c]);
4780         this.fireEvent('btnclick', btn.name, e);
4781     },
4782      /**
4783      * Set the title of the Dialog
4784      * @param {String} str new Title
4785      */
4786     setTitle: function(str) {
4787         this.titleEl.dom.innerHTML = str;
4788         this.title = str;
4789     },
4790     /**
4791      * Set the body of the Dialog
4792      * @param {String} str new Title
4793      */
4794     setBody: function(str) {
4795         this.bodyEl.dom.innerHTML = str;
4796     },
4797     /**
4798      * Set the body of the Dialog using the template
4799      * @param {Obj} data - apply this data to the template and replace the body contents.
4800      */
4801     applyBody: function(obj)
4802     {
4803         if (!this.tmpl) {
4804             Roo.log("Error - using apply Body without a template");
4805             //code
4806         }
4807         this.tmpl.overwrite(this.bodyEl, obj);
4808     },
4809     
4810     getChildHeight : function(child_nodes)
4811     {
4812         if(
4813             !child_nodes ||
4814             child_nodes.length == 0
4815         ) {
4816             return 0;
4817         }
4818         
4819         var child_height = 0;
4820         
4821         for(var i = 0; i < child_nodes.length; i++) {
4822             
4823             /*
4824             * for modal with tabs...
4825             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4826                 
4827                 var layout_childs = child_nodes[i].childNodes;
4828                 
4829                 for(var j = 0; j < layout_childs.length; j++) {
4830                     
4831                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4832                         
4833                         var layout_body_childs = layout_childs[j].childNodes;
4834                         
4835                         for(var k = 0; k < layout_body_childs.length; k++) {
4836                             
4837                             if(layout_body_childs[k].classList.contains('navbar')) {
4838                                 child_height += layout_body_childs[k].offsetHeight;
4839                                 continue;
4840                             }
4841                             
4842                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4843                                 
4844                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4845                                 
4846                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4847                                     
4848                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4849                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4850                                         continue;
4851                                     }
4852                                     
4853                                 }
4854                                 
4855                             }
4856                             
4857                         }
4858                     }
4859                 }
4860                 continue;
4861             }
4862             */
4863             
4864             child_height += child_nodes[i].offsetHeight;
4865             // Roo.log(child_nodes[i].offsetHeight);
4866         }
4867         
4868         return child_height;
4869     },
4870     toggleHeaderInput : function(is_edit)
4871     {
4872         if (!this.editableTitle) {
4873             return; // not editable.
4874         }
4875         if (is_edit && this.is_header_editing) {
4876             return; // already editing..
4877         }
4878         if (is_edit) {
4879     
4880             this.headerEditEl.dom.value = this.title;
4881             this.headerEditEl.removeClass('d-none');
4882             this.headerEditEl.dom.focus();
4883             this.titleEl.addClass('d-none');
4884             
4885             this.is_header_editing = true;
4886             return
4887         }
4888         // flip back to not editing.
4889         this.title = this.headerEditEl.dom.value;
4890         this.headerEditEl.addClass('d-none');
4891         this.titleEl.removeClass('d-none');
4892         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4893         this.is_header_editing = false;
4894         this.fireEvent('titlechanged', this, this.title);
4895     
4896             
4897         
4898     }
4899
4900 });
4901
4902
4903 Roo.apply(Roo.bootstrap.Modal,  {
4904     /**
4905          * Button config that displays a single OK button
4906          * @type Object
4907          */
4908         OK :  [{
4909             name : 'ok',
4910             weight : 'primary',
4911             html : 'OK'
4912         }],
4913         /**
4914          * Button config that displays Yes and No buttons
4915          * @type Object
4916          */
4917         YESNO : [
4918             {
4919                 name  : 'no',
4920                 html : 'No'
4921             },
4922             {
4923                 name  :'yes',
4924                 weight : 'primary',
4925                 html : 'Yes'
4926             }
4927         ],
4928
4929         /**
4930          * Button config that displays OK and Cancel buttons
4931          * @type Object
4932          */
4933         OKCANCEL : [
4934             {
4935                name : 'cancel',
4936                 html : 'Cancel'
4937             },
4938             {
4939                 name : 'ok',
4940                 weight : 'primary',
4941                 html : 'OK'
4942             }
4943         ],
4944         /**
4945          * Button config that displays Yes, No and Cancel buttons
4946          * @type Object
4947          */
4948         YESNOCANCEL : [
4949             {
4950                 name : 'yes',
4951                 weight : 'primary',
4952                 html : 'Yes'
4953             },
4954             {
4955                 name : 'no',
4956                 html : 'No'
4957             },
4958             {
4959                 name : 'cancel',
4960                 html : 'Cancel'
4961             }
4962         ],
4963         
4964         zIndex : 10001
4965 });
4966
4967 /*
4968  * - LGPL
4969  *
4970  * messagebox - can be used as a replace
4971  * 
4972  */
4973 /**
4974  * @class Roo.MessageBox
4975  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4976  * Example usage:
4977  *<pre><code>
4978 // Basic alert:
4979 Roo.Msg.alert('Status', 'Changes saved successfully.');
4980
4981 // Prompt for user data:
4982 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4983     if (btn == 'ok'){
4984         // process text value...
4985     }
4986 });
4987
4988 // Show a dialog using config options:
4989 Roo.Msg.show({
4990    title:'Save Changes?',
4991    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4992    buttons: Roo.Msg.YESNOCANCEL,
4993    fn: processResult,
4994    animEl: 'elId'
4995 });
4996 </code></pre>
4997  * @static
4998  */
4999 Roo.bootstrap.MessageBox = function(){
5000     var dlg, opt, mask, waitTimer;
5001     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5002     var buttons, activeTextEl, bwidth;
5003
5004     
5005     // private
5006     var handleButton = function(button){
5007         dlg.hide();
5008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5009     };
5010
5011     // private
5012     var handleHide = function(){
5013         if(opt && opt.cls){
5014             dlg.el.removeClass(opt.cls);
5015         }
5016         //if(waitTimer){
5017         //    Roo.TaskMgr.stop(waitTimer);
5018         //    waitTimer = null;
5019         //}
5020     };
5021
5022     // private
5023     var updateButtons = function(b){
5024         var width = 0;
5025         if(!b){
5026             buttons["ok"].hide();
5027             buttons["cancel"].hide();
5028             buttons["yes"].hide();
5029             buttons["no"].hide();
5030             dlg.footerEl.hide();
5031             
5032             return width;
5033         }
5034         dlg.footerEl.show();
5035         for(var k in buttons){
5036             if(typeof buttons[k] != "function"){
5037                 if(b[k]){
5038                     buttons[k].show();
5039                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5040                     width += buttons[k].el.getWidth()+15;
5041                 }else{
5042                     buttons[k].hide();
5043                 }
5044             }
5045         }
5046         return width;
5047     };
5048
5049     // private
5050     var handleEsc = function(d, k, e){
5051         if(opt && opt.closable !== false){
5052             dlg.hide();
5053         }
5054         if(e){
5055             e.stopEvent();
5056         }
5057     };
5058
5059     return {
5060         /**
5061          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5062          * @return {Roo.BasicDialog} The BasicDialog element
5063          */
5064         getDialog : function(){
5065            if(!dlg){
5066                 dlg = new Roo.bootstrap.Modal( {
5067                     //draggable: true,
5068                     //resizable:false,
5069                     //constraintoviewport:false,
5070                     //fixedcenter:true,
5071                     //collapsible : false,
5072                     //shim:true,
5073                     //modal: true,
5074                 //    width: 'auto',
5075                   //  height:100,
5076                     //buttonAlign:"center",
5077                     closeClick : function(){
5078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5079                             handleButton("no");
5080                         }else{
5081                             handleButton("cancel");
5082                         }
5083                     }
5084                 });
5085                 dlg.render();
5086                 dlg.on("hide", handleHide);
5087                 mask = dlg.mask;
5088                 //dlg.addKeyListener(27, handleEsc);
5089                 buttons = {};
5090                 this.buttons = buttons;
5091                 var bt = this.buttonText;
5092                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5093                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5094                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5095                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5096                 //Roo.log(buttons);
5097                 bodyEl = dlg.bodyEl.createChild({
5098
5099                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5100                         '<textarea class="roo-mb-textarea"></textarea>' +
5101                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5102                 });
5103                 msgEl = bodyEl.dom.firstChild;
5104                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5105                 textboxEl.enableDisplayMode();
5106                 textboxEl.addKeyListener([10,13], function(){
5107                     if(dlg.isVisible() && opt && opt.buttons){
5108                         if(opt.buttons.ok){
5109                             handleButton("ok");
5110                         }else if(opt.buttons.yes){
5111                             handleButton("yes");
5112                         }
5113                     }
5114                 });
5115                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5116                 textareaEl.enableDisplayMode();
5117                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5118                 progressEl.enableDisplayMode();
5119                 
5120                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5121                 var pf = progressEl.dom.firstChild;
5122                 if (pf) {
5123                     pp = Roo.get(pf.firstChild);
5124                     pp.setHeight(pf.offsetHeight);
5125                 }
5126                 
5127             }
5128             return dlg;
5129         },
5130
5131         /**
5132          * Updates the message box body text
5133          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5134          * the XHTML-compliant non-breaking space character '&amp;#160;')
5135          * @return {Roo.MessageBox} This message box
5136          */
5137         updateText : function(text)
5138         {
5139             if(!dlg.isVisible() && !opt.width){
5140                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5141                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5142             }
5143             msgEl.innerHTML = text || '&#160;';
5144       
5145             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5146             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5147             var w = Math.max(
5148                     Math.min(opt.width || cw , this.maxWidth), 
5149                     Math.max(opt.minWidth || this.minWidth, bwidth)
5150             );
5151             if(opt.prompt){
5152                 activeTextEl.setWidth(w);
5153             }
5154             if(dlg.isVisible()){
5155                 dlg.fixedcenter = false;
5156             }
5157             // to big, make it scroll. = But as usual stupid IE does not support
5158             // !important..
5159             
5160             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5161                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5162                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.height = '';
5165                 bodyEl.dom.style.overflowY = '';
5166             }
5167             if (cw > w) {
5168                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5169             } else {
5170                 bodyEl.dom.style.overflowX = '';
5171             }
5172             
5173             dlg.setContentSize(w, bodyEl.getHeight());
5174             if(dlg.isVisible()){
5175                 dlg.fixedcenter = true;
5176             }
5177             return this;
5178         },
5179
5180         /**
5181          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5182          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5183          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5184          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5185          * @return {Roo.MessageBox} This message box
5186          */
5187         updateProgress : function(value, text){
5188             if(text){
5189                 this.updateText(text);
5190             }
5191             
5192             if (pp) { // weird bug on my firefox - for some reason this is not defined
5193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5194                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5195             }
5196             return this;
5197         },        
5198
5199         /**
5200          * Returns true if the message box is currently displayed
5201          * @return {Boolean} True if the message box is visible, else false
5202          */
5203         isVisible : function(){
5204             return dlg && dlg.isVisible();  
5205         },
5206
5207         /**
5208          * Hides the message box if it is displayed
5209          */
5210         hide : function(){
5211             if(this.isVisible()){
5212                 dlg.hide();
5213             }  
5214         },
5215
5216         /**
5217          * Displays a new message box, or reinitializes an existing message box, based on the config options
5218          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5219          * The following config object properties are supported:
5220          * <pre>
5221 Property    Type             Description
5222 ----------  ---------------  ------------------------------------------------------------------------------------
5223 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5224                                    closes (defaults to undefined)
5225 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5226                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5227 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5228                                    progress and wait dialogs will ignore this property and always hide the
5229                                    close button as they can only be closed programmatically.
5230 cls               String           A custom CSS class to apply to the message box element
5231 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5232                                    displayed (defaults to 75)
5233 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5234                                    function will be btn (the name of the button that was clicked, if applicable,
5235                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5236                                    Progress and wait dialogs will ignore this option since they do not respond to
5237                                    user actions and can only be closed programmatically, so any required function
5238                                    should be called by the same code after it closes the dialog.
5239 icon              String           A CSS class that provides a background image to be used as an icon for
5240                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5241 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5242 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5243 modal             Boolean          False to allow user interaction with the page while the message box is
5244                                    displayed (defaults to true)
5245 msg               String           A string that will replace the existing message box body text (defaults
5246                                    to the XHTML-compliant non-breaking space character '&#160;')
5247 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5248 progress          Boolean          True to display a progress bar (defaults to false)
5249 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5250 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5251 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5252 title             String           The title text
5253 value             String           The string value to set into the active textbox element if displayed
5254 wait              Boolean          True to display a progress bar (defaults to false)
5255 width             Number           The width of the dialog in pixels
5256 </pre>
5257          *
5258          * Example usage:
5259          * <pre><code>
5260 Roo.Msg.show({
5261    title: 'Address',
5262    msg: 'Please enter your address:',
5263    width: 300,
5264    buttons: Roo.MessageBox.OKCANCEL,
5265    multiline: true,
5266    fn: saveAddress,
5267    animEl: 'addAddressBtn'
5268 });
5269 </code></pre>
5270          * @param {Object} config Configuration options
5271          * @return {Roo.MessageBox} This message box
5272          */
5273         show : function(options)
5274         {
5275             
5276             // this causes nightmares if you show one dialog after another
5277             // especially on callbacks..
5278              
5279             if(this.isVisible()){
5280                 
5281                 this.hide();
5282                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5283                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5284                 Roo.log("New Dialog Message:" +  options.msg )
5285                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5286                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5287                 
5288             }
5289             var d = this.getDialog();
5290             opt = options;
5291             d.setTitle(opt.title || "&#160;");
5292             d.closeEl.setDisplayed(opt.closable !== false);
5293             activeTextEl = textboxEl;
5294             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5295             if(opt.prompt){
5296                 if(opt.multiline){
5297                     textboxEl.hide();
5298                     textareaEl.show();
5299                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5300                         opt.multiline : this.defaultTextHeight);
5301                     activeTextEl = textareaEl;
5302                 }else{
5303                     textboxEl.show();
5304                     textareaEl.hide();
5305                 }
5306             }else{
5307                 textboxEl.hide();
5308                 textareaEl.hide();
5309             }
5310             progressEl.setDisplayed(opt.progress === true);
5311             if (opt.progress) {
5312                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5313             }
5314             this.updateProgress(0);
5315             activeTextEl.dom.value = opt.value || "";
5316             if(opt.prompt){
5317                 dlg.setDefaultButton(activeTextEl);
5318             }else{
5319                 var bs = opt.buttons;
5320                 var db = null;
5321                 if(bs && bs.ok){
5322                     db = buttons["ok"];
5323                 }else if(bs && bs.yes){
5324                     db = buttons["yes"];
5325                 }
5326                 dlg.setDefaultButton(db);
5327             }
5328             bwidth = updateButtons(opt.buttons);
5329             this.updateText(opt.msg);
5330             if(opt.cls){
5331                 d.el.addClass(opt.cls);
5332             }
5333             d.proxyDrag = opt.proxyDrag === true;
5334             d.modal = opt.modal !== false;
5335             d.mask = opt.modal !== false ? mask : false;
5336             if(!d.isVisible()){
5337                 // force it to the end of the z-index stack so it gets a cursor in FF
5338                 document.body.appendChild(dlg.el.dom);
5339                 d.animateTarget = null;
5340                 d.show(options.animEl);
5341             }
5342             return this;
5343         },
5344
5345         /**
5346          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5347          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5348          * and closing the message box when the process is complete.
5349          * @param {String} title The title bar text
5350          * @param {String} msg The message box body text
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         progress : function(title, msg){
5354             this.show({
5355                 title : title,
5356                 msg : msg,
5357                 buttons: false,
5358                 progress:true,
5359                 closable:false,
5360                 minWidth: this.minProgressWidth,
5361                 modal : true
5362             });
5363             return this;
5364         },
5365
5366         /**
5367          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5368          * If a callback function is passed it will be called after the user clicks the button, and the
5369          * id of the button that was clicked will be passed as the only parameter to the callback
5370          * (could also be the top-right close button).
5371          * @param {String} title The title bar text
5372          * @param {String} msg The message box body text
5373          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5374          * @param {Object} scope (optional) The scope of the callback function
5375          * @return {Roo.MessageBox} This message box
5376          */
5377         alert : function(title, msg, fn, scope)
5378         {
5379             this.show({
5380                 title : title,
5381                 msg : msg,
5382                 buttons: this.OK,
5383                 fn: fn,
5384                 closable : false,
5385                 scope : scope,
5386                 modal : true
5387             });
5388             return this;
5389         },
5390
5391         /**
5392          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5393          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5394          * You are responsible for closing the message box when the process is complete.
5395          * @param {String} msg The message box body text
5396          * @param {String} title (optional) The title bar text
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         wait : function(msg, title){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: false,
5404                 closable:false,
5405                 progress:true,
5406                 modal:true,
5407                 width:300,
5408                 wait:true
5409             });
5410             waitTimer = Roo.TaskMgr.start({
5411                 run: function(i){
5412                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5413                 },
5414                 interval: 1000
5415             });
5416             return this;
5417         },
5418
5419         /**
5420          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5421          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5422          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @return {Roo.MessageBox} This message box
5428          */
5429         confirm : function(title, msg, fn, scope){
5430             this.show({
5431                 title : title,
5432                 msg : msg,
5433                 buttons: this.YESNO,
5434                 fn: fn,
5435                 scope : scope,
5436                 modal : true
5437             });
5438             return this;
5439         },
5440
5441         /**
5442          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5443          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5444          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5445          * (could also be the top-right close button) and the text that was entered will be passed as the two
5446          * parameters to the callback.
5447          * @param {String} title The title bar text
5448          * @param {String} msg The message box body text
5449          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5450          * @param {Object} scope (optional) The scope of the callback function
5451          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5452          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5453          * @return {Roo.MessageBox} This message box
5454          */
5455         prompt : function(title, msg, fn, scope, multiline){
5456             this.show({
5457                 title : title,
5458                 msg : msg,
5459                 buttons: this.OKCANCEL,
5460                 fn: fn,
5461                 minWidth:250,
5462                 scope : scope,
5463                 prompt:true,
5464                 multiline: multiline,
5465                 modal : true
5466             });
5467             return this;
5468         },
5469
5470         /**
5471          * Button config that displays a single OK button
5472          * @type Object
5473          */
5474         OK : {ok:true},
5475         /**
5476          * Button config that displays Yes and No buttons
5477          * @type Object
5478          */
5479         YESNO : {yes:true, no:true},
5480         /**
5481          * Button config that displays OK and Cancel buttons
5482          * @type Object
5483          */
5484         OKCANCEL : {ok:true, cancel:true},
5485         /**
5486          * Button config that displays Yes, No and Cancel buttons
5487          * @type Object
5488          */
5489         YESNOCANCEL : {yes:true, no:true, cancel:true},
5490
5491         /**
5492          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5493          * @type Number
5494          */
5495         defaultTextHeight : 75,
5496         /**
5497          * The maximum width in pixels of the message box (defaults to 600)
5498          * @type Number
5499          */
5500         maxWidth : 600,
5501         /**
5502          * The minimum width in pixels of the message box (defaults to 100)
5503          * @type Number
5504          */
5505         minWidth : 100,
5506         /**
5507          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5508          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5509          * @type Number
5510          */
5511         minProgressWidth : 250,
5512         /**
5513          * An object containing the default button text strings that can be overriden for localized language support.
5514          * Supported properties are: ok, cancel, yes and no.
5515          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5516          * @type Object
5517          */
5518         buttonText : {
5519             ok : "OK",
5520             cancel : "Cancel",
5521             yes : "Yes",
5522             no : "No"
5523         }
5524     };
5525 }();
5526
5527 /**
5528  * Shorthand for {@link Roo.MessageBox}
5529  */
5530 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5531 Roo.Msg = Roo.Msg || Roo.MessageBox;
5532 /*
5533  * - LGPL
5534  *
5535  * navbar
5536  * 
5537  */
5538
5539 /**
5540  * @class Roo.bootstrap.nav.Bar
5541  * @extends Roo.bootstrap.Component
5542  * @abstract
5543  * Bootstrap Navbar class
5544
5545  * @constructor
5546  * Create a new Navbar
5547  * @param {Object} config The config object
5548  */
5549
5550
5551 Roo.bootstrap.nav.Bar = function(config){
5552     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5553     this.addEvents({
5554         // raw events
5555         /**
5556          * @event beforetoggle
5557          * Fire before toggle the menu
5558          * @param {Roo.EventObject} e
5559          */
5560         "beforetoggle" : true
5561     });
5562 };
5563
5564 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5565     
5566     
5567    
5568     // private
5569     navItems : false,
5570     loadMask : false,
5571     
5572     
5573     getAutoCreate : function(){
5574         
5575         
5576         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5577         
5578     },
5579     
5580     initEvents :function ()
5581     {
5582         //Roo.log(this.el.select('.navbar-toggle',true));
5583         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5584         
5585         var mark = {
5586             tag: "div",
5587             cls:"x-dlg-mask"
5588         };
5589         
5590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5591         
5592         var size = this.el.getSize();
5593         this.maskEl.setSize(size.width, size.height);
5594         this.maskEl.enableDisplayMode("block");
5595         this.maskEl.hide();
5596         
5597         if(this.loadMask){
5598             this.maskEl.show();
5599         }
5600     },
5601     
5602     
5603     getChildContainer : function()
5604     {
5605         if (this.el && this.el.select('.collapse').getCount()) {
5606             return this.el.select('.collapse',true).first();
5607         }
5608         
5609         return this.el;
5610     },
5611     
5612     mask : function()
5613     {
5614         this.maskEl.show();
5615     },
5616     
5617     unmask : function()
5618     {
5619         this.maskEl.hide();
5620     },
5621     onToggle : function()
5622     {
5623         
5624         if(this.fireEvent('beforetoggle', this) === false){
5625             return;
5626         }
5627         var ce = this.el.select('.navbar-collapse',true).first();
5628       
5629         if (!ce.hasClass('show')) {
5630            this.expand();
5631         } else {
5632             this.collapse();
5633         }
5634         
5635         
5636     
5637     },
5638     /**
5639      * Expand the navbar pulldown 
5640      */
5641     expand : function ()
5642     {
5643        
5644         var ce = this.el.select('.navbar-collapse',true).first();
5645         if (ce.hasClass('collapsing')) {
5646             return;
5647         }
5648         ce.dom.style.height = '';
5649                // show it...
5650         ce.addClass('in'); // old...
5651         ce.removeClass('collapse');
5652         ce.addClass('show');
5653         var h = ce.getHeight();
5654         Roo.log(h);
5655         ce.removeClass('show');
5656         // at this point we should be able to see it..
5657         ce.addClass('collapsing');
5658         
5659         ce.setHeight(0); // resize it ...
5660         ce.on('transitionend', function() {
5661             //Roo.log('done transition');
5662             ce.removeClass('collapsing');
5663             ce.addClass('show');
5664             ce.removeClass('collapse');
5665
5666             ce.dom.style.height = '';
5667         }, this, { single: true} );
5668         ce.setHeight(h);
5669         ce.dom.scrollTop = 0;
5670     },
5671     /**
5672      * Collapse the navbar pulldown 
5673      */
5674     collapse : function()
5675     {
5676          var ce = this.el.select('.navbar-collapse',true).first();
5677        
5678         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5679             // it's collapsed or collapsing..
5680             return;
5681         }
5682         ce.removeClass('in'); // old...
5683         ce.setHeight(ce.getHeight());
5684         ce.removeClass('show');
5685         ce.addClass('collapsing');
5686         
5687         ce.on('transitionend', function() {
5688             ce.dom.style.height = '';
5689             ce.removeClass('collapsing');
5690             ce.addClass('collapse');
5691         }, this, { single: true} );
5692         ce.setHeight(0);
5693     }
5694     
5695     
5696     
5697 });
5698
5699
5700
5701  
5702
5703  /*
5704  * - LGPL
5705  *
5706  * navbar
5707  * 
5708  */
5709
5710 /**
5711  * @class Roo.bootstrap.nav.Simplebar
5712  * @extends Roo.bootstrap.nav.Bar
5713  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5714  * Bootstrap Sidebar class
5715  *
5716  * @cfg {Boolean} inverse is inverted color
5717  * 
5718  * @cfg {String} type (nav | pills | tabs)
5719  * @cfg {Boolean} arrangement stacked | justified
5720  * @cfg {String} align (left | right) alignment
5721  * 
5722  * @cfg {Boolean} main (true|false) main nav bar? default false
5723  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5724  * 
5725  * @cfg {String} tag (header|footer|nav|div) default is nav 
5726
5727  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5728  * 
5729  * 
5730  * @constructor
5731  * Create a new Sidebar
5732  * @param {Object} config The config object
5733  */
5734
5735
5736 Roo.bootstrap.nav.Simplebar = function(config){
5737     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5738 };
5739
5740 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5741     
5742     inverse: false,
5743     
5744     type: false,
5745     arrangement: '',
5746     align : false,
5747     
5748     weight : 'light',
5749     
5750     main : false,
5751     
5752     
5753     tag : false,
5754     
5755     
5756     getAutoCreate : function(){
5757         
5758         
5759         var cfg = {
5760             tag : this.tag || 'div',
5761             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5762         };
5763         if (['light','white'].indexOf(this.weight) > -1) {
5764             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5765         }
5766         cfg.cls += ' bg-' + this.weight;
5767         
5768         if (this.inverse) {
5769             cfg.cls += ' navbar-inverse';
5770             
5771         }
5772         
5773         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5774         
5775         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5776             return cfg;
5777         }
5778         
5779         
5780     
5781         
5782         cfg.cn = [
5783             {
5784                 cls: 'nav nav-' + this.xtype,
5785                 tag : 'ul'
5786             }
5787         ];
5788         
5789          
5790         this.type = this.type || 'nav';
5791         if (['tabs','pills'].indexOf(this.type) != -1) {
5792             cfg.cn[0].cls += ' nav-' + this.type
5793         
5794         
5795         } else {
5796             if (this.type!=='nav') {
5797                 Roo.log('nav type must be nav/tabs/pills')
5798             }
5799             cfg.cn[0].cls += ' navbar-nav'
5800         }
5801         
5802         
5803         
5804         
5805         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5806             cfg.cn[0].cls += ' nav-' + this.arrangement;
5807         }
5808         
5809         
5810         if (this.align === 'right') {
5811             cfg.cn[0].cls += ' navbar-right';
5812         }
5813         
5814         
5815         
5816         
5817         return cfg;
5818     
5819         
5820     }
5821     
5822     
5823     
5824 });
5825
5826
5827
5828  
5829
5830  
5831        /*
5832  * - LGPL
5833  *
5834  * navbar
5835  * navbar-fixed-top
5836  * navbar-expand-md  fixed-top 
5837  */
5838
5839 /**
5840  * @class Roo.bootstrap.nav.Headerbar
5841  * @extends Roo.bootstrap.nav.Simplebar
5842  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5843  * Bootstrap Sidebar class
5844  *
5845  * @cfg {String} brand what is brand
5846  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5847  * @cfg {String} brand_href href of the brand
5848  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5849  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5850  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5851  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5852  * 
5853  * @constructor
5854  * Create a new Sidebar
5855  * @param {Object} config The config object
5856  */
5857
5858
5859 Roo.bootstrap.nav.Headerbar = function(config){
5860     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5861       
5862 };
5863
5864 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5865     
5866     position: '',
5867     brand: '',
5868     brand_href: false,
5869     srButton : true,
5870     autohide : false,
5871     desktopCenter : false,
5872    
5873     
5874     getAutoCreate : function(){
5875         
5876         var   cfg = {
5877             tag: this.nav || 'nav',
5878             cls: 'navbar navbar-expand-md',
5879             role: 'navigation',
5880             cn: []
5881         };
5882         
5883         var cn = cfg.cn;
5884         if (this.desktopCenter) {
5885             cn.push({cls : 'container', cn : []});
5886             cn = cn[0].cn;
5887         }
5888         
5889         if(this.srButton){
5890             var btn = {
5891                 tag: 'button',
5892                 type: 'button',
5893                 cls: 'navbar-toggle navbar-toggler',
5894                 'data-toggle': 'collapse',
5895                 cn: [
5896                     {
5897                         tag: 'span',
5898                         cls: 'sr-only',
5899                         html: 'Toggle navigation'
5900                     },
5901                     {
5902                         tag: 'span',
5903                         cls: 'icon-bar navbar-toggler-icon'
5904                     },
5905                     {
5906                         tag: 'span',
5907                         cls: 'icon-bar'
5908                     },
5909                     {
5910                         tag: 'span',
5911                         cls: 'icon-bar'
5912                     }
5913                 ]
5914             };
5915             
5916             cn.push( Roo.bootstrap.version == 4 ? btn : {
5917                 tag: 'div',
5918                 cls: 'navbar-header',
5919                 cn: [
5920                     btn
5921                 ]
5922             });
5923         }
5924         
5925         cn.push({
5926             tag: 'div',
5927             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5928             cn : []
5929         });
5930         
5931         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5932         
5933         if (['light','white'].indexOf(this.weight) > -1) {
5934             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5935         }
5936         cfg.cls += ' bg-' + this.weight;
5937         
5938         
5939         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5940             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5941             
5942             // tag can override this..
5943             
5944             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5945         }
5946         
5947         if (this.brand !== '') {
5948             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5949             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5950                 tag: 'a',
5951                 href: this.brand_href ? this.brand_href : '#',
5952                 cls: 'navbar-brand',
5953                 cn: [
5954                 this.brand
5955                 ]
5956             });
5957         }
5958         
5959         if(this.main){
5960             cfg.cls += ' main-nav';
5961         }
5962         
5963         
5964         return cfg;
5965
5966         
5967     },
5968     getHeaderChildContainer : function()
5969     {
5970         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5971             return this.el.select('.navbar-header',true).first();
5972         }
5973         
5974         return this.getChildContainer();
5975     },
5976     
5977     getChildContainer : function()
5978     {
5979          
5980         return this.el.select('.roo-navbar-collapse',true).first();
5981          
5982         
5983     },
5984     
5985     initEvents : function()
5986     {
5987         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5988         
5989         if (this.autohide) {
5990             
5991             var prevScroll = 0;
5992             var ft = this.el;
5993             
5994             Roo.get(document).on('scroll',function(e) {
5995                 var ns = Roo.get(document).getScroll().top;
5996                 var os = prevScroll;
5997                 prevScroll = ns;
5998                 
5999                 if(ns > os){
6000                     ft.removeClass('slideDown');
6001                     ft.addClass('slideUp');
6002                     return;
6003                 }
6004                 ft.removeClass('slideUp');
6005                 ft.addClass('slideDown');
6006                  
6007               
6008           },this);
6009         }
6010     }    
6011     
6012 });
6013
6014
6015
6016  
6017
6018  /*
6019  * - LGPL
6020  *
6021  * navbar
6022  * 
6023  */
6024
6025 /**
6026  * @class Roo.bootstrap.nav.Sidebar
6027  * @extends Roo.bootstrap.nav.Bar
6028  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6029  * Bootstrap Sidebar class
6030  * 
6031  * @constructor
6032  * Create a new Sidebar
6033  * @param {Object} config The config object
6034  */
6035
6036
6037 Roo.bootstrap.nav.Sidebar = function(config){
6038     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6039 };
6040
6041 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6042     
6043     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6044     
6045     getAutoCreate : function(){
6046         
6047         
6048         return  {
6049             tag: 'div',
6050             cls: 'sidebar sidebar-nav'
6051         };
6052     
6053         
6054     }
6055     
6056     
6057     
6058 });
6059
6060
6061
6062  
6063
6064  /*
6065  * - LGPL
6066  *
6067  * nav group
6068  * 
6069  */
6070
6071 /**
6072  * @class Roo.bootstrap.nav.Group
6073  * @extends Roo.bootstrap.Component
6074  * @children Roo.bootstrap.nav.Item
6075  * Bootstrap NavGroup class
6076  * @cfg {String} align (left|right)
6077  * @cfg {Boolean} inverse
6078  * @cfg {String} type (nav|pills|tab) default nav
6079  * @cfg {String} navId - reference Id for navbar.
6080  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6081  * 
6082  * @constructor
6083  * Create a new nav group
6084  * @param {Object} config The config object
6085  */
6086
6087 Roo.bootstrap.nav.Group = function(config){
6088     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6089     this.navItems = [];
6090    
6091     Roo.bootstrap.nav.Group.register(this);
6092      this.addEvents({
6093         /**
6094              * @event changed
6095              * Fires when the active item changes
6096              * @param {Roo.bootstrap.nav.Group} this
6097              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6098              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6099          */
6100         'changed': true
6101      });
6102     
6103 };
6104
6105 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6106     
6107     align: '',
6108     inverse: false,
6109     form: false,
6110     type: 'nav',
6111     navId : '',
6112     // private
6113     pilltype : true,
6114     
6115     navItems : false, 
6116     
6117     getAutoCreate : function()
6118     {
6119         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6120         
6121         cfg = {
6122             tag : 'ul',
6123             cls: 'nav' 
6124         };
6125         if (Roo.bootstrap.version == 4) {
6126             if (['tabs','pills'].indexOf(this.type) != -1) {
6127                 cfg.cls += ' nav-' + this.type; 
6128             } else {
6129                 // trying to remove so header bar can right align top?
6130                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6131                     // do not use on header bar... 
6132                     cfg.cls += ' navbar-nav';
6133                 }
6134             }
6135             
6136         } else {
6137             if (['tabs','pills'].indexOf(this.type) != -1) {
6138                 cfg.cls += ' nav-' + this.type
6139             } else {
6140                 if (this.type !== 'nav') {
6141                     Roo.log('nav type must be nav/tabs/pills')
6142                 }
6143                 cfg.cls += ' navbar-nav'
6144             }
6145         }
6146         
6147         if (this.parent() && this.parent().sidebar) {
6148             cfg = {
6149                 tag: 'ul',
6150                 cls: 'dashboard-menu sidebar-menu'
6151             };
6152             
6153             return cfg;
6154         }
6155         
6156         if (this.form === true) {
6157             cfg = {
6158                 tag: 'form',
6159                 cls: 'navbar-form form-inline'
6160             };
6161             //nav navbar-right ml-md-auto
6162             if (this.align === 'right') {
6163                 cfg.cls += ' navbar-right ml-md-auto';
6164             } else {
6165                 cfg.cls += ' navbar-left';
6166             }
6167         }
6168         
6169         if (this.align === 'right') {
6170             cfg.cls += ' navbar-right ml-md-auto';
6171         } else {
6172             cfg.cls += ' mr-auto';
6173         }
6174         
6175         if (this.inverse) {
6176             cfg.cls += ' navbar-inverse';
6177             
6178         }
6179         
6180         
6181         return cfg;
6182     },
6183     /**
6184     * sets the active Navigation item
6185     * @param {Roo.bootstrap.nav.Item} the new current navitem
6186     */
6187     setActiveItem : function(item)
6188     {
6189         var prev = false;
6190         Roo.each(this.navItems, function(v){
6191             if (v == item) {
6192                 return ;
6193             }
6194             if (v.isActive()) {
6195                 v.setActive(false, true);
6196                 prev = v;
6197                 
6198             }
6199             
6200         });
6201
6202         item.setActive(true, true);
6203         this.fireEvent('changed', this, item, prev);
6204         
6205         
6206     },
6207     /**
6208     * gets the active Navigation item
6209     * @return {Roo.bootstrap.nav.Item} the current navitem
6210     */
6211     getActive : function()
6212     {
6213         
6214         var prev = false;
6215         Roo.each(this.navItems, function(v){
6216             
6217             if (v.isActive()) {
6218                 prev = v;
6219                 
6220             }
6221             
6222         });
6223         return prev;
6224     },
6225     
6226     indexOfNav : function()
6227     {
6228         
6229         var prev = false;
6230         Roo.each(this.navItems, function(v,i){
6231             
6232             if (v.isActive()) {
6233                 prev = i;
6234                 
6235             }
6236             
6237         });
6238         return prev;
6239     },
6240     /**
6241     * adds a Navigation item
6242     * @param {Roo.bootstrap.nav.Item} the navitem to add
6243     */
6244     addItem : function(cfg)
6245     {
6246         if (this.form && Roo.bootstrap.version == 4) {
6247             cfg.tag = 'div';
6248         }
6249         var cn = new Roo.bootstrap.nav.Item(cfg);
6250         this.register(cn);
6251         cn.parentId = this.id;
6252         cn.onRender(this.el, null);
6253         return cn;
6254     },
6255     /**
6256     * register a Navigation item
6257     * @param {Roo.bootstrap.nav.Item} the navitem to add
6258     */
6259     register : function(item)
6260     {
6261         this.navItems.push( item);
6262         item.navId = this.navId;
6263     
6264     },
6265     
6266     /**
6267     * clear all the Navigation item
6268     */
6269    
6270     clearAll : function()
6271     {
6272         this.navItems = [];
6273         this.el.dom.innerHTML = '';
6274     },
6275     
6276     getNavItem: function(tabId)
6277     {
6278         var ret = false;
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId == tabId) {
6281                ret =  e;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287         return ret;
6288     },
6289     
6290     setActiveNext : function()
6291     {
6292         var i = this.indexOfNav(this.getActive());
6293         if (i > this.navItems.length) {
6294             return;
6295         }
6296         this.setActiveItem(this.navItems[i+1]);
6297     },
6298     setActivePrev : function()
6299     {
6300         var i = this.indexOfNav(this.getActive());
6301         if (i  < 1) {
6302             return;
6303         }
6304         this.setActiveItem(this.navItems[i-1]);
6305     },
6306     clearWasActive : function(except) {
6307         Roo.each(this.navItems, function(e) {
6308             if (e.tabId != except.tabId && e.was_active) {
6309                e.was_active = false;
6310                return false;
6311             }
6312             return true;
6313             
6314         });
6315     },
6316     getWasActive : function ()
6317     {
6318         var r = false;
6319         Roo.each(this.navItems, function(e) {
6320             if (e.was_active) {
6321                r = e;
6322                return false;
6323             }
6324             return true;
6325             
6326         });
6327         return r;
6328     }
6329     
6330     
6331 });
6332
6333  
6334 Roo.apply(Roo.bootstrap.nav.Group, {
6335     
6336     groups: {},
6337      /**
6338     * register a Navigation Group
6339     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6340     */
6341     register : function(navgrp)
6342     {
6343         this.groups[navgrp.navId] = navgrp;
6344         
6345     },
6346     /**
6347     * fetch a Navigation Group based on the navigation ID
6348     * @param {string} the navgroup to add
6349     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6350     */
6351     get: function(navId) {
6352         if (typeof(this.groups[navId]) == 'undefined') {
6353             return false;
6354             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6355         }
6356         return this.groups[navId] ;
6357     }
6358     
6359     
6360     
6361 });
6362
6363  /**
6364  * @class Roo.bootstrap.nav.Item
6365  * @extends Roo.bootstrap.Component
6366  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6367  * @parent Roo.bootstrap.nav.Group
6368  * @licence LGPL
6369  * Bootstrap Navbar.NavItem class
6370  * 
6371  * @cfg {String} href  link to
6372  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6373  * @cfg {Boolean} button_outline show and outlined button
6374  * @cfg {String} html content of button
6375  * @cfg {String} badge text inside badge
6376  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6377  * @cfg {String} glyphicon DEPRICATED - use fa
6378  * @cfg {String} icon DEPRICATED - use fa
6379  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6380  * @cfg {Boolean} active Is item active
6381  * @cfg {Boolean} disabled Is item disabled
6382  * @cfg {String} linkcls  Link Class
6383  * @cfg {Boolean} preventDefault (true | false) default false
6384  * @cfg {String} tabId the tab that this item activates.
6385  * @cfg {String} tagtype (a|span) render as a href or span?
6386  * @cfg {Boolean} animateRef (true|false) link to element default false  
6387  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6388   
6389  * @constructor
6390  * Create a new Navbar Item
6391  * @param {Object} config The config object
6392  */
6393 Roo.bootstrap.nav.Item = function(config){
6394     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6395     this.addEvents({
6396         // raw events
6397         /**
6398          * @event click
6399          * The raw click event for the entire grid.
6400          * @param {Roo.EventObject} e
6401          */
6402         "click" : true,
6403          /**
6404             * @event changed
6405             * Fires when the active item active state changes
6406             * @param {Roo.bootstrap.nav.Item} this
6407             * @param {boolean} state the new state
6408              
6409          */
6410         'changed': true,
6411         /**
6412             * @event scrollto
6413             * Fires when scroll to element
6414             * @param {Roo.bootstrap.nav.Item} this
6415             * @param {Object} options
6416             * @param {Roo.EventObject} e
6417              
6418          */
6419         'scrollto': true
6420     });
6421    
6422 };
6423
6424 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6425     
6426     href: false,
6427     html: '',
6428     badge: '',
6429     icon: false,
6430     fa : false,
6431     glyphicon: false,
6432     active: false,
6433     preventDefault : false,
6434     tabId : false,
6435     tagtype : 'a',
6436     tag: 'li',
6437     disabled : false,
6438     animateRef : false,
6439     was_active : false,
6440     button_weight : '',
6441     button_outline : false,
6442     linkcls : '',
6443     navLink: false,
6444     
6445     getAutoCreate : function(){
6446          
6447         var cfg = {
6448             tag: this.tag,
6449             cls: 'nav-item'
6450         };
6451         
6452         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6453         
6454         if (this.active) {
6455             cfg.cls +=  ' active' ;
6456         }
6457         if (this.disabled) {
6458             cfg.cls += ' disabled';
6459         }
6460         
6461         // BS4 only?
6462         if (this.button_weight.length) {
6463             cfg.tag = this.href ? 'a' : 'button';
6464             cfg.html = this.html || '';
6465             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6466             if (this.href) {
6467                 cfg.href = this.href;
6468             }
6469             if (this.fa) {
6470                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6471             } else {
6472                 cfg.cls += " nav-html";
6473             }
6474             
6475             // menu .. should add dropdown-menu class - so no need for carat..
6476             
6477             if (this.badge !== '') {
6478                  
6479                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6480             }
6481             return cfg;
6482         }
6483         
6484         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6485             cfg.cn = [
6486                 {
6487                     tag: this.tagtype,
6488                     href : this.href || "#",
6489                     html: this.html || '',
6490                     cls : ''
6491                 }
6492             ];
6493             if (this.tagtype == 'a') {
6494                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6495         
6496             }
6497             if (this.icon) {
6498                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6499             } else  if (this.fa) {
6500                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6501             } else if(this.glyphicon) {
6502                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6503             } else {
6504                 cfg.cn[0].cls += " nav-html";
6505             }
6506             
6507             if (this.menu) {
6508                 cfg.cn[0].html += " <span class='caret'></span>";
6509              
6510             }
6511             
6512             if (this.badge !== '') {
6513                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6514             }
6515         }
6516         
6517         
6518         
6519         return cfg;
6520     },
6521     onRender : function(ct, position)
6522     {
6523        // Roo.log("Call onRender: " + this.xtype);
6524         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6525             this.tag = 'div';
6526         }
6527         
6528         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6529         this.navLink = this.el.select('.nav-link',true).first();
6530         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6531         return ret;
6532     },
6533       
6534     
6535     initEvents: function() 
6536     {
6537         if (typeof (this.menu) != 'undefined') {
6538             this.menu.parentType = this.xtype;
6539             this.menu.triggerEl = this.el;
6540             this.menu = this.addxtype(Roo.apply({}, this.menu));
6541         }
6542         
6543         this.el.on('click', this.onClick, this);
6544         
6545         //if(this.tagtype == 'span'){
6546         //    this.el.select('span',true).on('click', this.onClick, this);
6547         //}
6548        
6549         // at this point parent should be available..
6550         this.parent().register(this);
6551     },
6552     
6553     onClick : function(e)
6554     {
6555         if (e.getTarget('.dropdown-menu-item')) {
6556             // did you click on a menu itemm.... - then don't trigger onclick..
6557             return;
6558         }
6559         
6560         if(
6561                 this.preventDefault ||
6562                                 this.href === false ||
6563                 this.href === '#' 
6564         ){
6565             //Roo.log("NavItem - prevent Default?");
6566             e.preventDefault();
6567         }
6568         
6569         if (this.disabled) {
6570             return;
6571         }
6572         
6573         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6574         if (tg && tg.transition) {
6575             Roo.log("waiting for the transitionend");
6576             return;
6577         }
6578         
6579         
6580         
6581         //Roo.log("fire event clicked");
6582         if(this.fireEvent('click', this, e) === false){
6583             return;
6584         };
6585         
6586         if(this.tagtype == 'span'){
6587             return;
6588         }
6589         
6590         //Roo.log(this.href);
6591         var ael = this.el.select('a',true).first();
6592         //Roo.log(ael);
6593         
6594         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6595             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6596             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6597                 return; // ignore... - it's a 'hash' to another page.
6598             }
6599             Roo.log("NavItem - prevent Default?");
6600             e.preventDefault();
6601             this.scrollToElement(e);
6602         }
6603         
6604         
6605         var p =  this.parent();
6606    
6607         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6608             if (typeof(p.setActiveItem) !== 'undefined') {
6609                 p.setActiveItem(this);
6610             }
6611         }
6612         
6613         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6614         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6615             // remove the collapsed menu expand...
6616             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6617         }
6618     },
6619     
6620     isActive: function () {
6621         return this.active
6622     },
6623     setActive : function(state, fire, is_was_active)
6624     {
6625         if (this.active && !state && this.navId) {
6626             this.was_active = true;
6627             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6628             if (nv) {
6629                 nv.clearWasActive(this);
6630             }
6631             
6632         }
6633         this.active = state;
6634         
6635         if (!state ) {
6636             this.el.removeClass('active');
6637             this.navLink ? this.navLink.removeClass('active') : false;
6638         } else if (!this.el.hasClass('active')) {
6639             
6640             this.el.addClass('active');
6641             if (Roo.bootstrap.version == 4 && this.navLink ) {
6642                 this.navLink.addClass('active');
6643             }
6644             
6645         }
6646         if (fire) {
6647             this.fireEvent('changed', this, state);
6648         }
6649         
6650         // show a panel if it's registered and related..
6651         
6652         if (!this.navId || !this.tabId || !state || is_was_active) {
6653             return;
6654         }
6655         
6656         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6657         if (!tg) {
6658             return;
6659         }
6660         var pan = tg.getPanelByName(this.tabId);
6661         if (!pan) {
6662             return;
6663         }
6664         // if we can not flip to new panel - go back to old nav highlight..
6665         if (false == tg.showPanel(pan)) {
6666             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6667             if (nv) {
6668                 var onav = nv.getWasActive();
6669                 if (onav) {
6670                     onav.setActive(true, false, true);
6671                 }
6672             }
6673             
6674         }
6675         
6676         
6677         
6678     },
6679      // this should not be here...
6680     setDisabled : function(state)
6681     {
6682         this.disabled = state;
6683         if (!state ) {
6684             this.el.removeClass('disabled');
6685         } else if (!this.el.hasClass('disabled')) {
6686             this.el.addClass('disabled');
6687         }
6688         
6689     },
6690     
6691     /**
6692      * Fetch the element to display the tooltip on.
6693      * @return {Roo.Element} defaults to this.el
6694      */
6695     tooltipEl : function()
6696     {
6697         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6698     },
6699     
6700     scrollToElement : function(e)
6701     {
6702         var c = document.body;
6703         
6704         /*
6705          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6706          */
6707         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6708             c = document.documentElement;
6709         }
6710         
6711         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6712         
6713         if(!target){
6714             return;
6715         }
6716
6717         var o = target.calcOffsetsTo(c);
6718         
6719         var options = {
6720             target : target,
6721             value : o[1]
6722         };
6723         
6724         this.fireEvent('scrollto', this, options, e);
6725         
6726         Roo.get(c).scrollTo('top', options.value, true);
6727         
6728         return;
6729     },
6730     /**
6731      * Set the HTML (text content) of the item
6732      * @param {string} html  content for the nav item
6733      */
6734     setHtml : function(html)
6735     {
6736         this.html = html;
6737         this.htmlEl.dom.innerHTML = html;
6738         
6739     } 
6740 });
6741  
6742
6743  /*
6744  * - LGPL
6745  *
6746  * sidebar item
6747  *
6748  *  li
6749  *    <span> icon </span>
6750  *    <span> text </span>
6751  *    <span>badge </span>
6752  */
6753
6754 /**
6755  * @class Roo.bootstrap.nav.SidebarItem
6756  * @extends Roo.bootstrap.nav.Item
6757  * Bootstrap Navbar.NavSidebarItem class
6758  * 
6759  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6760  * {Boolean} open is the menu open
6761  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6762  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6763  * {String} buttonSize (sm|md|lg)the extra classes for the button
6764  * {Boolean} showArrow show arrow next to the text (default true)
6765  * @constructor
6766  * Create a new Navbar Button
6767  * @param {Object} config The config object
6768  */
6769 Roo.bootstrap.nav.SidebarItem = function(config){
6770     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6771     this.addEvents({
6772         // raw events
6773         /**
6774          * @event click
6775          * The raw click event for the entire grid.
6776          * @param {Roo.EventObject} e
6777          */
6778         "click" : true,
6779          /**
6780             * @event changed
6781             * Fires when the active item active state changes
6782             * @param {Roo.bootstrap.nav.SidebarItem} this
6783             * @param {boolean} state the new state
6784              
6785          */
6786         'changed': true
6787     });
6788    
6789 };
6790
6791 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6792     
6793     badgeWeight : 'default',
6794     
6795     open: false,
6796     
6797     buttonView : false,
6798     
6799     buttonWeight : 'default',
6800     
6801     buttonSize : 'md',
6802     
6803     showArrow : true,
6804     
6805     getAutoCreate : function(){
6806         
6807         
6808         var a = {
6809                 tag: 'a',
6810                 href : this.href || '#',
6811                 cls: '',
6812                 html : '',
6813                 cn : []
6814         };
6815         
6816         if(this.buttonView){
6817             a = {
6818                 tag: 'button',
6819                 href : this.href || '#',
6820                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6821                 html : this.html,
6822                 cn : []
6823             };
6824         }
6825         
6826         var cfg = {
6827             tag: 'li',
6828             cls: '',
6829             cn: [ a ]
6830         };
6831         
6832         if (this.active) {
6833             cfg.cls += ' active';
6834         }
6835         
6836         if (this.disabled) {
6837             cfg.cls += ' disabled';
6838         }
6839         if (this.open) {
6840             cfg.cls += ' open x-open';
6841         }
6842         // left icon..
6843         if (this.glyphicon || this.icon) {
6844             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6845             a.cn.push({ tag : 'i', cls : c }) ;
6846         }
6847         
6848         if(!this.buttonView){
6849             var span = {
6850                 tag: 'span',
6851                 html : this.html || ''
6852             };
6853
6854             a.cn.push(span);
6855             
6856         }
6857         
6858         if (this.badge !== '') {
6859             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6860         }
6861         
6862         if (this.menu) {
6863             
6864             if(this.showArrow){
6865                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6866             }
6867             
6868             a.cls += ' dropdown-toggle treeview' ;
6869         }
6870         
6871         return cfg;
6872     },
6873     
6874     initEvents : function()
6875     { 
6876         if (typeof (this.menu) != 'undefined') {
6877             this.menu.parentType = this.xtype;
6878             this.menu.triggerEl = this.el;
6879             this.menu = this.addxtype(Roo.apply({}, this.menu));
6880         }
6881         
6882         this.el.on('click', this.onClick, this);
6883         
6884         if(this.badge !== ''){
6885             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6886         }
6887         
6888     },
6889     
6890     onClick : function(e)
6891     {
6892         if(this.disabled){
6893             e.preventDefault();
6894             return;
6895         }
6896         
6897         if(this.preventDefault){
6898             e.preventDefault();
6899         }
6900         
6901         this.fireEvent('click', this, e);
6902     },
6903     
6904     disable : function()
6905     {
6906         this.setDisabled(true);
6907     },
6908     
6909     enable : function()
6910     {
6911         this.setDisabled(false);
6912     },
6913     
6914     setDisabled : function(state)
6915     {
6916         if(this.disabled == state){
6917             return;
6918         }
6919         
6920         this.disabled = state;
6921         
6922         if (state) {
6923             this.el.addClass('disabled');
6924             return;
6925         }
6926         
6927         this.el.removeClass('disabled');
6928         
6929         return;
6930     },
6931     
6932     setActive : function(state)
6933     {
6934         if(this.active == state){
6935             return;
6936         }
6937         
6938         this.active = state;
6939         
6940         if (state) {
6941             this.el.addClass('active');
6942             return;
6943         }
6944         
6945         this.el.removeClass('active');
6946         
6947         return;
6948     },
6949     
6950     isActive: function () 
6951     {
6952         return this.active;
6953     },
6954     
6955     setBadge : function(str)
6956     {
6957         if(!this.badgeEl){
6958             return;
6959         }
6960         
6961         this.badgeEl.dom.innerHTML = str;
6962     }
6963     
6964    
6965      
6966  
6967 });
6968  
6969
6970  /*
6971  * - LGPL
6972  *
6973  * nav progress bar
6974  * 
6975  */
6976
6977 /**
6978  * @class Roo.bootstrap.nav.ProgressBar
6979  * @extends Roo.bootstrap.Component
6980  * @children Roo.bootstrap.nav.ProgressBarItem
6981  * Bootstrap NavProgressBar class
6982  * 
6983  * @constructor
6984  * Create a new nav progress bar - a bar indicating step along a process
6985  * @param {Object} config The config object
6986  */
6987
6988 Roo.bootstrap.nav.ProgressBar = function(config){
6989     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6990
6991     this.bullets = this.bullets || [];
6992    
6993 //    Roo.bootstrap.nav.ProgressBar.register(this);
6994      this.addEvents({
6995         /**
6996              * @event changed
6997              * Fires when the active item changes
6998              * @param {Roo.bootstrap.nav.ProgressBar} this
6999              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7000              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7001          */
7002         'changed': true
7003      });
7004     
7005 };
7006
7007 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7008     /**
7009      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7010      * Bullets for the Nav Progress bar for the toolbar
7011      */
7012     bullets : [],
7013     barItems : [],
7014     
7015     getAutoCreate : function()
7016     {
7017         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7018         
7019         cfg = {
7020             tag : 'div',
7021             cls : 'roo-navigation-bar-group',
7022             cn : [
7023                 {
7024                     tag : 'div',
7025                     cls : 'roo-navigation-top-bar'
7026                 },
7027                 {
7028                     tag : 'div',
7029                     cls : 'roo-navigation-bullets-bar',
7030                     cn : [
7031                         {
7032                             tag : 'ul',
7033                             cls : 'roo-navigation-bar'
7034                         }
7035                     ]
7036                 },
7037                 
7038                 {
7039                     tag : 'div',
7040                     cls : 'roo-navigation-bottom-bar'
7041                 }
7042             ]
7043             
7044         };
7045         
7046         return cfg;
7047         
7048     },
7049     
7050     initEvents: function() 
7051     {
7052         
7053     },
7054     
7055     onRender : function(ct, position) 
7056     {
7057         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7058         
7059         if(this.bullets.length){
7060             Roo.each(this.bullets, function(b){
7061                this.addItem(b);
7062             }, this);
7063         }
7064         
7065         this.format();
7066         
7067     },
7068     
7069     addItem : function(cfg)
7070     {
7071         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7072         
7073         item.parentId = this.id;
7074         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7075         
7076         if(cfg.html){
7077             var top = new Roo.bootstrap.Element({
7078                 tag : 'div',
7079                 cls : 'roo-navigation-bar-text'
7080             });
7081             
7082             var bottom = new Roo.bootstrap.Element({
7083                 tag : 'div',
7084                 cls : 'roo-navigation-bar-text'
7085             });
7086             
7087             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7088             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7089             
7090             var topText = new Roo.bootstrap.Element({
7091                 tag : 'span',
7092                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7093             });
7094             
7095             var bottomText = new Roo.bootstrap.Element({
7096                 tag : 'span',
7097                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7098             });
7099             
7100             topText.onRender(top.el, null);
7101             bottomText.onRender(bottom.el, null);
7102             
7103             item.topEl = top;
7104             item.bottomEl = bottom;
7105         }
7106         
7107         this.barItems.push(item);
7108         
7109         return item;
7110     },
7111     
7112     getActive : function()
7113     {
7114         var active = false;
7115         
7116         Roo.each(this.barItems, function(v){
7117             
7118             if (!v.isActive()) {
7119                 return;
7120             }
7121             
7122             active = v;
7123             return false;
7124             
7125         });
7126         
7127         return active;
7128     },
7129     
7130     setActiveItem : function(item)
7131     {
7132         var prev = false;
7133         
7134         Roo.each(this.barItems, function(v){
7135             if (v.rid == item.rid) {
7136                 return ;
7137             }
7138             
7139             if (v.isActive()) {
7140                 v.setActive(false);
7141                 prev = v;
7142             }
7143         });
7144
7145         item.setActive(true);
7146         
7147         this.fireEvent('changed', this, item, prev);
7148     },
7149     
7150     getBarItem: function(rid)
7151     {
7152         var ret = false;
7153         
7154         Roo.each(this.barItems, function(e) {
7155             if (e.rid != rid) {
7156                 return;
7157             }
7158             
7159             ret =  e;
7160             return false;
7161         });
7162         
7163         return ret;
7164     },
7165     
7166     indexOfItem : function(item)
7167     {
7168         var index = false;
7169         
7170         Roo.each(this.barItems, function(v, i){
7171             
7172             if (v.rid != item.rid) {
7173                 return;
7174             }
7175             
7176             index = i;
7177             return false
7178         });
7179         
7180         return index;
7181     },
7182     
7183     setActiveNext : function()
7184     {
7185         var i = this.indexOfItem(this.getActive());
7186         
7187         if (i > this.barItems.length) {
7188             return;
7189         }
7190         
7191         this.setActiveItem(this.barItems[i+1]);
7192     },
7193     
7194     setActivePrev : function()
7195     {
7196         var i = this.indexOfItem(this.getActive());
7197         
7198         if (i  < 1) {
7199             return;
7200         }
7201         
7202         this.setActiveItem(this.barItems[i-1]);
7203     },
7204     
7205     format : function()
7206     {
7207         if(!this.barItems.length){
7208             return;
7209         }
7210      
7211         var width = 100 / this.barItems.length;
7212         
7213         Roo.each(this.barItems, function(i){
7214             i.el.setStyle('width', width + '%');
7215             i.topEl.el.setStyle('width', width + '%');
7216             i.bottomEl.el.setStyle('width', width + '%');
7217         }, this);
7218         
7219     }
7220     
7221 });
7222 /*
7223  * - LGPL
7224  *
7225  * Nav Progress Item
7226  * 
7227  */
7228
7229 /**
7230  * @class Roo.bootstrap.nav.ProgressBarItem
7231  * @extends Roo.bootstrap.Component
7232  * Bootstrap NavProgressBarItem class
7233  * @cfg {String} rid the reference id
7234  * @cfg {Boolean} active (true|false) Is item active default false
7235  * @cfg {Boolean} disabled (true|false) Is item active default false
7236  * @cfg {String} html
7237  * @cfg {String} position (top|bottom) text position default bottom
7238  * @cfg {String} icon show icon instead of number
7239  * 
7240  * @constructor
7241  * Create a new NavProgressBarItem
7242  * @param {Object} config The config object
7243  */
7244 Roo.bootstrap.nav.ProgressBarItem = function(config){
7245     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7246     this.addEvents({
7247         // raw events
7248         /**
7249          * @event click
7250          * The raw click event for the entire grid.
7251          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7252          * @param {Roo.EventObject} e
7253          */
7254         "click" : true
7255     });
7256    
7257 };
7258
7259 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7260     
7261     rid : '',
7262     active : false,
7263     disabled : false,
7264     html : '',
7265     position : 'bottom',
7266     icon : false,
7267     
7268     getAutoCreate : function()
7269     {
7270         var iconCls = 'roo-navigation-bar-item-icon';
7271         
7272         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7273         
7274         var cfg = {
7275             tag: 'li',
7276             cls: 'roo-navigation-bar-item',
7277             cn : [
7278                 {
7279                     tag : 'i',
7280                     cls : iconCls
7281                 }
7282             ]
7283         };
7284         
7285         if(this.active){
7286             cfg.cls += ' active';
7287         }
7288         if(this.disabled){
7289             cfg.cls += ' disabled';
7290         }
7291         
7292         return cfg;
7293     },
7294     
7295     disable : function()
7296     {
7297         this.setDisabled(true);
7298     },
7299     
7300     enable : function()
7301     {
7302         this.setDisabled(false);
7303     },
7304     
7305     initEvents: function() 
7306     {
7307         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7308         
7309         this.iconEl.on('click', this.onClick, this);
7310     },
7311     
7312     onClick : function(e)
7313     {
7314         e.preventDefault();
7315         
7316         if(this.disabled){
7317             return;
7318         }
7319         
7320         if(this.fireEvent('click', this, e) === false){
7321             return;
7322         };
7323         
7324         this.parent().setActiveItem(this);
7325     },
7326     
7327     isActive: function () 
7328     {
7329         return this.active;
7330     },
7331     
7332     setActive : function(state)
7333     {
7334         if(this.active == state){
7335             return;
7336         }
7337         
7338         this.active = state;
7339         
7340         if (state) {
7341             this.el.addClass('active');
7342             return;
7343         }
7344         
7345         this.el.removeClass('active');
7346         
7347         return;
7348     },
7349     
7350     setDisabled : function(state)
7351     {
7352         if(this.disabled == state){
7353             return;
7354         }
7355         
7356         this.disabled = state;
7357         
7358         if (state) {
7359             this.el.addClass('disabled');
7360             return;
7361         }
7362         
7363         this.el.removeClass('disabled');
7364     },
7365     
7366     tooltipEl : function()
7367     {
7368         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7369     }
7370 });
7371  
7372
7373  /*
7374  * - LGPL
7375  *
7376  *  Breadcrumb Nav
7377  * 
7378  */
7379 Roo.namespace('Roo.bootstrap.breadcrumb');
7380
7381
7382 /**
7383  * @class Roo.bootstrap.breadcrumb.Nav
7384  * @extends Roo.bootstrap.Component
7385  * Bootstrap Breadcrumb Nav Class
7386  *  
7387  * @children Roo.bootstrap.breadcrumb.Item
7388  * 
7389  * @constructor
7390  * Create a new breadcrumb.Nav
7391  * @param {Object} config The config object
7392  */
7393
7394
7395 Roo.bootstrap.breadcrumb.Nav = function(config){
7396     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7397     
7398     
7399 };
7400
7401 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7402     
7403     getAutoCreate : function()
7404     {
7405
7406         var cfg = {
7407             tag: 'nav',
7408             cn : [
7409                 {
7410                     tag : 'ol',
7411                     cls : 'breadcrumb'
7412                 }
7413             ]
7414             
7415         };
7416           
7417         return cfg;
7418     },
7419     
7420     initEvents: function()
7421     {
7422         this.olEl = this.el.select('ol',true).first();    
7423     },
7424     getChildContainer : function()
7425     {
7426         return this.olEl;  
7427     }
7428     
7429 });
7430
7431  /*
7432  * - LGPL
7433  *
7434  *  Breadcrumb Item
7435  * 
7436  */
7437
7438
7439 /**
7440  * @class Roo.bootstrap.breadcrumb.Nav
7441  * @extends Roo.bootstrap.Component
7442  * @children Roo.bootstrap.Component
7443  * @parent Roo.bootstrap.breadcrumb.Nav
7444  * Bootstrap Breadcrumb Nav Class
7445  *  
7446  * 
7447  * @cfg {String} html the content of the link.
7448  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7449  * @cfg {Boolean} active is it active
7450
7451  * 
7452  * @constructor
7453  * Create a new breadcrumb.Nav
7454  * @param {Object} config The config object
7455  */
7456
7457 Roo.bootstrap.breadcrumb.Item = function(config){
7458     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7459     this.addEvents({
7460         // img events
7461         /**
7462          * @event click
7463          * The img click event for the img.
7464          * @param {Roo.EventObject} e
7465          */
7466         "click" : true
7467     });
7468     
7469 };
7470
7471 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7472     
7473     href: false,
7474     html : '',
7475     
7476     getAutoCreate : function()
7477     {
7478
7479         var cfg = {
7480             tag: 'li',
7481             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7482         };
7483         if (this.href !== false) {
7484             cfg.cn = [{
7485                 tag : 'a',
7486                 href : this.href,
7487                 html : this.html
7488             }];
7489         } else {
7490             cfg.html = this.html;
7491         }
7492         
7493         return cfg;
7494     },
7495     
7496     initEvents: function()
7497     {
7498         if (this.href) {
7499             this.el.select('a', true).first().on('click',this.onClick, this)
7500         }
7501         
7502     },
7503     onClick : function(e)
7504     {
7505         e.preventDefault();
7506         this.fireEvent('click',this,  e);
7507     }
7508     
7509 });
7510
7511  /*
7512  * - LGPL
7513  *
7514  * row
7515  * 
7516  */
7517
7518 /**
7519  * @class Roo.bootstrap.Row
7520  * @extends Roo.bootstrap.Component
7521  * @children Roo.bootstrap.Component
7522  * Bootstrap Row class (contains columns...)
7523  * 
7524  * @constructor
7525  * Create a new Row
7526  * @param {Object} config The config object
7527  */
7528
7529 Roo.bootstrap.Row = function(config){
7530     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7531 };
7532
7533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7534     
7535     getAutoCreate : function(){
7536        return {
7537             cls: 'row clearfix'
7538        };
7539     }
7540     
7541     
7542 });
7543
7544  
7545
7546  /*
7547  * - LGPL
7548  *
7549  * pagination
7550  * 
7551  */
7552
7553 /**
7554  * @class Roo.bootstrap.Pagination
7555  * @extends Roo.bootstrap.Component
7556  * @children Roo.bootstrap.Pagination
7557  * Bootstrap Pagination class
7558  * 
7559  * @cfg {String} size (xs|sm|md|lg|xl)
7560  * @cfg {Boolean} inverse 
7561  * 
7562  * @constructor
7563  * Create a new Pagination
7564  * @param {Object} config The config object
7565  */
7566
7567 Roo.bootstrap.Pagination = function(config){
7568     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7569 };
7570
7571 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7572     
7573     cls: false,
7574     size: false,
7575     inverse: false,
7576     
7577     getAutoCreate : function(){
7578         var cfg = {
7579             tag: 'ul',
7580                 cls: 'pagination'
7581         };
7582         if (this.inverse) {
7583             cfg.cls += ' inverse';
7584         }
7585         if (this.html) {
7586             cfg.html=this.html;
7587         }
7588         if (this.cls) {
7589             cfg.cls += " " + this.cls;
7590         }
7591         return cfg;
7592     }
7593    
7594 });
7595
7596  
7597
7598  /*
7599  * - LGPL
7600  *
7601  * Pagination item
7602  * 
7603  */
7604
7605
7606 /**
7607  * @class Roo.bootstrap.PaginationItem
7608  * @extends Roo.bootstrap.Component
7609  * Bootstrap PaginationItem class
7610  * @cfg {String} html text
7611  * @cfg {String} href the link
7612  * @cfg {Boolean} preventDefault (true | false) default true
7613  * @cfg {Boolean} active (true | false) default false
7614  * @cfg {Boolean} disabled default false
7615  * 
7616  * 
7617  * @constructor
7618  * Create a new PaginationItem
7619  * @param {Object} config The config object
7620  */
7621
7622
7623 Roo.bootstrap.PaginationItem = function(config){
7624     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7625     this.addEvents({
7626         // raw events
7627         /**
7628          * @event click
7629          * The raw click event for the entire grid.
7630          * @param {Roo.EventObject} e
7631          */
7632         "click" : true
7633     });
7634 };
7635
7636 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7637     
7638     href : false,
7639     html : false,
7640     preventDefault: true,
7641     active : false,
7642     cls : false,
7643     disabled: false,
7644     
7645     getAutoCreate : function(){
7646         var cfg= {
7647             tag: 'li',
7648             cn: [
7649                 {
7650                     tag : 'a',
7651                     href : this.href ? this.href : '#',
7652                     html : this.html ? this.html : ''
7653                 }
7654             ]
7655         };
7656         
7657         if(this.cls){
7658             cfg.cls = this.cls;
7659         }
7660         
7661         if(this.disabled){
7662             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7663         }
7664         
7665         if(this.active){
7666             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7667         }
7668         
7669         return cfg;
7670     },
7671     
7672     initEvents: function() {
7673         
7674         this.el.on('click', this.onClick, this);
7675         
7676     },
7677     onClick : function(e)
7678     {
7679         Roo.log('PaginationItem on click ');
7680         if(this.preventDefault){
7681             e.preventDefault();
7682         }
7683         
7684         if(this.disabled){
7685             return;
7686         }
7687         
7688         this.fireEvent('click', this, e);
7689     }
7690    
7691 });
7692
7693  
7694
7695  /*
7696  * - LGPL
7697  *
7698  * slider
7699  * 
7700  */
7701
7702
7703 /**
7704  * @class Roo.bootstrap.Slider
7705  * @extends Roo.bootstrap.Component
7706  * Bootstrap Slider class
7707  *    
7708  * @constructor
7709  * Create a new Slider
7710  * @param {Object} config The config object
7711  */
7712
7713 Roo.bootstrap.Slider = function(config){
7714     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7715 };
7716
7717 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7718     
7719     getAutoCreate : function(){
7720         
7721         var cfg = {
7722             tag: 'div',
7723             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7724             cn: [
7725                 {
7726                     tag: 'a',
7727                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7728                 }
7729             ]
7730         };
7731         
7732         return cfg;
7733     }
7734    
7735 });
7736
7737  /*
7738  * Based on:
7739  * Ext JS Library 1.1.1
7740  * Copyright(c) 2006-2007, Ext JS, LLC.
7741  *
7742  * Originally Released Under LGPL - original licence link has changed is not relivant.
7743  *
7744  * Fork - LGPL
7745  * <script type="text/javascript">
7746  */
7747  /**
7748  * @extends Roo.dd.DDProxy
7749  * @class Roo.grid.SplitDragZone
7750  * Support for Column Header resizing
7751  * @constructor
7752  * @param {Object} config
7753  */
7754 // private
7755 // This is a support class used internally by the Grid components
7756 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7757     this.grid = grid;
7758     this.view = grid.getView();
7759     this.proxy = this.view.resizeProxy;
7760     Roo.grid.SplitDragZone.superclass.constructor.call(
7761         this,
7762         hd, // ID
7763         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7764         {  // CONFIG
7765             dragElId : Roo.id(this.proxy.dom),
7766             resizeFrame:false
7767         }
7768     );
7769     
7770     this.setHandleElId(Roo.id(hd));
7771     if (hd2 !== false) {
7772         this.setOuterHandleElId(Roo.id(hd2));
7773     }
7774     
7775     this.scroll = false;
7776 };
7777 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7778     fly: Roo.Element.fly,
7779
7780     b4StartDrag : function(x, y){
7781         this.view.headersDisabled = true;
7782         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7783                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7784         );
7785         this.proxy.setHeight(h);
7786         
7787         // for old system colWidth really stored the actual width?
7788         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7789         // which in reality did not work.. - it worked only for fixed sizes
7790         // for resizable we need to use actual sizes.
7791         var w = this.cm.getColumnWidth(this.cellIndex);
7792         if (!this.view.mainWrap) {
7793             // bootstrap.
7794             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7795         }
7796         
7797         
7798         
7799         // this was w-this.grid.minColumnWidth;
7800         // doesnt really make sense? - w = thie curren width or the rendered one?
7801         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7802         this.resetConstraints();
7803         this.setXConstraint(minw, 1000);
7804         this.setYConstraint(0, 0);
7805         this.minX = x - minw;
7806         this.maxX = x + 1000;
7807         this.startPos = x;
7808         if (!this.view.mainWrap) { // this is Bootstrap code..
7809             this.getDragEl().style.display='block';
7810         }
7811         
7812         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7813     },
7814
7815
7816     handleMouseDown : function(e){
7817         ev = Roo.EventObject.setEvent(e);
7818         var t = this.fly(ev.getTarget());
7819         if(t.hasClass("x-grid-split")){
7820             this.cellIndex = this.view.getCellIndex(t.dom);
7821             this.split = t.dom;
7822             this.cm = this.grid.colModel;
7823             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7824                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7825             }
7826         }
7827     },
7828
7829     endDrag : function(e){
7830         this.view.headersDisabled = false;
7831         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7832         var diff = endX - this.startPos;
7833         // 
7834         var w = this.cm.getColumnWidth(this.cellIndex);
7835         if (!this.view.mainWrap) {
7836             w = 0;
7837         }
7838         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7839     },
7840
7841     autoOffset : function(){
7842         this.setDelta(0,0);
7843     }
7844 });/*
7845  * Based on:
7846  * Ext JS Library 1.1.1
7847  * Copyright(c) 2006-2007, Ext JS, LLC.
7848  *
7849  * Originally Released Under LGPL - original licence link has changed is not relivant.
7850  *
7851  * Fork - LGPL
7852  * <script type="text/javascript">
7853  */
7854
7855 /**
7856  * @class Roo.grid.AbstractSelectionModel
7857  * @extends Roo.util.Observable
7858  * @abstract
7859  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7860  * implemented by descendant classes.  This class should not be directly instantiated.
7861  * @constructor
7862  */
7863 Roo.grid.AbstractSelectionModel = function(){
7864     this.locked = false;
7865     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7866 };
7867
7868 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7869     /** @ignore Called by the grid automatically. Do not call directly. */
7870     init : function(grid){
7871         this.grid = grid;
7872         this.initEvents();
7873     },
7874
7875     /**
7876      * Locks the selections.
7877      */
7878     lock : function(){
7879         this.locked = true;
7880     },
7881
7882     /**
7883      * Unlocks the selections.
7884      */
7885     unlock : function(){
7886         this.locked = false;
7887     },
7888
7889     /**
7890      * Returns true if the selections are locked.
7891      * @return {Boolean}
7892      */
7893     isLocked : function(){
7894         return this.locked;
7895     }
7896 });/*
7897  * Based on:
7898  * Ext JS Library 1.1.1
7899  * Copyright(c) 2006-2007, Ext JS, LLC.
7900  *
7901  * Originally Released Under LGPL - original licence link has changed is not relivant.
7902  *
7903  * Fork - LGPL
7904  * <script type="text/javascript">
7905  */
7906 /**
7907  * @extends Roo.grid.AbstractSelectionModel
7908  * @class Roo.grid.RowSelectionModel
7909  * The default SelectionModel used by {@link Roo.grid.Grid}.
7910  * It supports multiple selections and keyboard selection/navigation. 
7911  * @constructor
7912  * @param {Object} config
7913  */
7914 Roo.grid.RowSelectionModel = function(config){
7915     Roo.apply(this, config);
7916     this.selections = new Roo.util.MixedCollection(false, function(o){
7917         return o.id;
7918     });
7919
7920     this.last = false;
7921     this.lastActive = false;
7922
7923     this.addEvents({
7924         /**
7925         * @event selectionchange
7926         * Fires when the selection changes
7927         * @param {SelectionModel} this
7928         */
7929        "selectionchange" : true,
7930        /**
7931         * @event afterselectionchange
7932         * Fires after the selection changes (eg. by key press or clicking)
7933         * @param {SelectionModel} this
7934         */
7935        "afterselectionchange" : true,
7936        /**
7937         * @event beforerowselect
7938         * Fires when a row is selected being selected, return false to cancel.
7939         * @param {SelectionModel} this
7940         * @param {Number} rowIndex The selected index
7941         * @param {Boolean} keepExisting False if other selections will be cleared
7942         */
7943        "beforerowselect" : true,
7944        /**
7945         * @event rowselect
7946         * Fires when a row is selected.
7947         * @param {SelectionModel} this
7948         * @param {Number} rowIndex The selected index
7949         * @param {Roo.data.Record} r The record
7950         */
7951        "rowselect" : true,
7952        /**
7953         * @event rowdeselect
7954         * Fires when a row is deselected.
7955         * @param {SelectionModel} this
7956         * @param {Number} rowIndex The selected index
7957         */
7958         "rowdeselect" : true
7959     });
7960     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7961     this.locked = false;
7962 };
7963
7964 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7965     /**
7966      * @cfg {Boolean} singleSelect
7967      * True to allow selection of only one row at a time (defaults to false)
7968      */
7969     singleSelect : false,
7970
7971     // private
7972     initEvents : function(){
7973
7974         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7975             this.grid.on("mousedown", this.handleMouseDown, this);
7976         }else{ // allow click to work like normal
7977             this.grid.on("rowclick", this.handleDragableRowClick, this);
7978         }
7979         // bootstrap does not have a view..
7980         var view = this.grid.view ? this.grid.view : this.grid;
7981         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7982             "up" : function(e){
7983                 if(!e.shiftKey){
7984                     this.selectPrevious(e.shiftKey);
7985                 }else if(this.last !== false && this.lastActive !== false){
7986                     var last = this.last;
7987                     this.selectRange(this.last,  this.lastActive-1);
7988                     view.focusRow(this.lastActive);
7989                     if(last !== false){
7990                         this.last = last;
7991                     }
7992                 }else{
7993                     this.selectFirstRow();
7994                 }
7995                 this.fireEvent("afterselectionchange", this);
7996             },
7997             "down" : function(e){
7998                 if(!e.shiftKey){
7999                     this.selectNext(e.shiftKey);
8000                 }else if(this.last !== false && this.lastActive !== false){
8001                     var last = this.last;
8002                     this.selectRange(this.last,  this.lastActive+1);
8003                     view.focusRow(this.lastActive);
8004                     if(last !== false){
8005                         this.last = last;
8006                     }
8007                 }else{
8008                     this.selectFirstRow();
8009                 }
8010                 this.fireEvent("afterselectionchange", this);
8011             },
8012             scope: this
8013         });
8014
8015          
8016         view.on("refresh", this.onRefresh, this);
8017         view.on("rowupdated", this.onRowUpdated, this);
8018         view.on("rowremoved", this.onRemove, this);
8019     },
8020
8021     // private
8022     onRefresh : function(){
8023         var ds = this.grid.ds, i, v = this.grid.view;
8024         var s = this.selections;
8025         s.each(function(r){
8026             if((i = ds.indexOfId(r.id)) != -1){
8027                 v.onRowSelect(i);
8028                 s.add(ds.getAt(i)); // updating the selection relate data
8029             }else{
8030                 s.remove(r);
8031             }
8032         });
8033     },
8034
8035     // private
8036     onRemove : function(v, index, r){
8037         this.selections.remove(r);
8038     },
8039
8040     // private
8041     onRowUpdated : function(v, index, r){
8042         if(this.isSelected(r)){
8043             v.onRowSelect(index);
8044         }
8045     },
8046
8047     /**
8048      * Select records.
8049      * @param {Array} records The records to select
8050      * @param {Boolean} keepExisting (optional) True to keep existing selections
8051      */
8052     selectRecords : function(records, keepExisting){
8053         if(!keepExisting){
8054             this.clearSelections();
8055         }
8056         var ds = this.grid.ds;
8057         for(var i = 0, len = records.length; i < len; i++){
8058             this.selectRow(ds.indexOf(records[i]), true);
8059         }
8060     },
8061
8062     /**
8063      * Gets the number of selected rows.
8064      * @return {Number}
8065      */
8066     getCount : function(){
8067         return this.selections.length;
8068     },
8069
8070     /**
8071      * Selects the first row in the grid.
8072      */
8073     selectFirstRow : function(){
8074         this.selectRow(0);
8075     },
8076
8077     /**
8078      * Select the last row.
8079      * @param {Boolean} keepExisting (optional) True to keep existing selections
8080      */
8081     selectLastRow : function(keepExisting){
8082         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8083     },
8084
8085     /**
8086      * Selects the row immediately following the last selected row.
8087      * @param {Boolean} keepExisting (optional) True to keep existing selections
8088      */
8089     selectNext : function(keepExisting){
8090         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8091             this.selectRow(this.last+1, keepExisting);
8092             var view = this.grid.view ? this.grid.view : this.grid;
8093             view.focusRow(this.last);
8094         }
8095     },
8096
8097     /**
8098      * Selects the row that precedes the last selected row.
8099      * @param {Boolean} keepExisting (optional) True to keep existing selections
8100      */
8101     selectPrevious : function(keepExisting){
8102         if(this.last){
8103             this.selectRow(this.last-1, keepExisting);
8104             var view = this.grid.view ? this.grid.view : this.grid;
8105             view.focusRow(this.last);
8106         }
8107     },
8108
8109     /**
8110      * Returns the selected records
8111      * @return {Array} Array of selected records
8112      */
8113     getSelections : function(){
8114         return [].concat(this.selections.items);
8115     },
8116
8117     /**
8118      * Returns the first selected record.
8119      * @return {Record}
8120      */
8121     getSelected : function(){
8122         return this.selections.itemAt(0);
8123     },
8124
8125
8126     /**
8127      * Clears all selections.
8128      */
8129     clearSelections : function(fast){
8130         if(this.locked) {
8131             return;
8132         }
8133         if(fast !== true){
8134             var ds = this.grid.ds;
8135             var s = this.selections;
8136             s.each(function(r){
8137                 this.deselectRow(ds.indexOfId(r.id));
8138             }, this);
8139             s.clear();
8140         }else{
8141             this.selections.clear();
8142         }
8143         this.last = false;
8144     },
8145
8146
8147     /**
8148      * Selects all rows.
8149      */
8150     selectAll : function(){
8151         if(this.locked) {
8152             return;
8153         }
8154         this.selections.clear();
8155         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8156             this.selectRow(i, true);
8157         }
8158     },
8159
8160     /**
8161      * Returns True if there is a selection.
8162      * @return {Boolean}
8163      */
8164     hasSelection : function(){
8165         return this.selections.length > 0;
8166     },
8167
8168     /**
8169      * Returns True if the specified row is selected.
8170      * @param {Number/Record} record The record or index of the record to check
8171      * @return {Boolean}
8172      */
8173     isSelected : function(index){
8174         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8175         return (r && this.selections.key(r.id) ? true : false);
8176     },
8177
8178     /**
8179      * Returns True if the specified record id is selected.
8180      * @param {String} id The id of record to check
8181      * @return {Boolean}
8182      */
8183     isIdSelected : function(id){
8184         return (this.selections.key(id) ? true : false);
8185     },
8186
8187     // private
8188     handleMouseDown : function(e, t)
8189     {
8190         var view = this.grid.view ? this.grid.view : this.grid;
8191         var rowIndex;
8192         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8193             return;
8194         };
8195         if(e.shiftKey && this.last !== false){
8196             var last = this.last;
8197             this.selectRange(last, rowIndex, e.ctrlKey);
8198             this.last = last; // reset the last
8199             view.focusRow(rowIndex);
8200         }else{
8201             var isSelected = this.isSelected(rowIndex);
8202             if(e.button !== 0 && isSelected){
8203                 view.focusRow(rowIndex);
8204             }else if(e.ctrlKey && isSelected){
8205                 this.deselectRow(rowIndex);
8206             }else if(!isSelected){
8207                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8208                 view.focusRow(rowIndex);
8209             }
8210         }
8211         this.fireEvent("afterselectionchange", this);
8212     },
8213     // private
8214     handleDragableRowClick :  function(grid, rowIndex, e) 
8215     {
8216         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8217             this.selectRow(rowIndex, false);
8218             var view = this.grid.view ? this.grid.view : this.grid;
8219             view.focusRow(rowIndex);
8220              this.fireEvent("afterselectionchange", this);
8221         }
8222     },
8223     
8224     /**
8225      * Selects multiple rows.
8226      * @param {Array} rows Array of the indexes of the row to select
8227      * @param {Boolean} keepExisting (optional) True to keep existing selections
8228      */
8229     selectRows : function(rows, keepExisting){
8230         if(!keepExisting){
8231             this.clearSelections();
8232         }
8233         for(var i = 0, len = rows.length; i < len; i++){
8234             this.selectRow(rows[i], true);
8235         }
8236     },
8237
8238     /**
8239      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8240      * @param {Number} startRow The index of the first row in the range
8241      * @param {Number} endRow The index of the last row in the range
8242      * @param {Boolean} keepExisting (optional) True to retain existing selections
8243      */
8244     selectRange : function(startRow, endRow, keepExisting){
8245         if(this.locked) {
8246             return;
8247         }
8248         if(!keepExisting){
8249             this.clearSelections();
8250         }
8251         if(startRow <= endRow){
8252             for(var i = startRow; i <= endRow; i++){
8253                 this.selectRow(i, true);
8254             }
8255         }else{
8256             for(var i = startRow; i >= endRow; i--){
8257                 this.selectRow(i, true);
8258             }
8259         }
8260     },
8261
8262     /**
8263      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8264      * @param {Number} startRow The index of the first row in the range
8265      * @param {Number} endRow The index of the last row in the range
8266      */
8267     deselectRange : function(startRow, endRow, preventViewNotify){
8268         if(this.locked) {
8269             return;
8270         }
8271         for(var i = startRow; i <= endRow; i++){
8272             this.deselectRow(i, preventViewNotify);
8273         }
8274     },
8275
8276     /**
8277      * Selects a row.
8278      * @param {Number} row The index of the row to select
8279      * @param {Boolean} keepExisting (optional) True to keep existing selections
8280      */
8281     selectRow : function(index, keepExisting, preventViewNotify){
8282         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8283             return;
8284         }
8285         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8286             if(!keepExisting || this.singleSelect){
8287                 this.clearSelections();
8288             }
8289             var r = this.grid.ds.getAt(index);
8290             this.selections.add(r);
8291             this.last = this.lastActive = index;
8292             if(!preventViewNotify){
8293                 var view = this.grid.view ? this.grid.view : this.grid;
8294                 view.onRowSelect(index);
8295             }
8296             this.fireEvent("rowselect", this, index, r);
8297             this.fireEvent("selectionchange", this);
8298         }
8299     },
8300
8301     /**
8302      * Deselects a row.
8303      * @param {Number} row The index of the row to deselect
8304      */
8305     deselectRow : function(index, preventViewNotify){
8306         if(this.locked) {
8307             return;
8308         }
8309         if(this.last == index){
8310             this.last = false;
8311         }
8312         if(this.lastActive == index){
8313             this.lastActive = false;
8314         }
8315         var r = this.grid.ds.getAt(index);
8316         this.selections.remove(r);
8317         if(!preventViewNotify){
8318             var view = this.grid.view ? this.grid.view : this.grid;
8319             view.onRowDeselect(index);
8320         }
8321         this.fireEvent("rowdeselect", this, index);
8322         this.fireEvent("selectionchange", this);
8323     },
8324
8325     // private
8326     restoreLast : function(){
8327         if(this._last){
8328             this.last = this._last;
8329         }
8330     },
8331
8332     // private
8333     acceptsNav : function(row, col, cm){
8334         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8335     },
8336
8337     // private
8338     onEditorKey : function(field, e){
8339         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8340         if(k == e.TAB){
8341             e.stopEvent();
8342             ed.completeEdit();
8343             if(e.shiftKey){
8344                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8345             }else{
8346                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8347             }
8348         }else if(k == e.ENTER && !e.ctrlKey){
8349             e.stopEvent();
8350             ed.completeEdit();
8351             if(e.shiftKey){
8352                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8353             }else{
8354                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8355             }
8356         }else if(k == e.ESC){
8357             ed.cancelEdit();
8358         }
8359         if(newCell){
8360             g.startEditing(newCell[0], newCell[1]);
8361         }
8362     }
8363 });/*
8364  * Based on:
8365  * Ext JS Library 1.1.1
8366  * Copyright(c) 2006-2007, Ext JS, LLC.
8367  *
8368  * Originally Released Under LGPL - original licence link has changed is not relivant.
8369  *
8370  * Fork - LGPL
8371  * <script type="text/javascript">
8372  */
8373  
8374
8375 /**
8376  * @class Roo.grid.ColumnModel
8377  * @extends Roo.util.Observable
8378  * This is the default implementation of a ColumnModel used by the Grid. It defines
8379  * the columns in the grid.
8380  * <br>Usage:<br>
8381  <pre><code>
8382  var colModel = new Roo.grid.ColumnModel([
8383         {header: "Ticker", width: 60, sortable: true, locked: true},
8384         {header: "Company Name", width: 150, sortable: true},
8385         {header: "Market Cap.", width: 100, sortable: true},
8386         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8387         {header: "Employees", width: 100, sortable: true, resizable: false}
8388  ]);
8389  </code></pre>
8390  * <p>
8391  
8392  * The config options listed for this class are options which may appear in each
8393  * individual column definition.
8394  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8395  * @constructor
8396  * @param {Object} config An Array of column config objects. See this class's
8397  * config objects for details.
8398 */
8399 Roo.grid.ColumnModel = function(config){
8400         /**
8401      * The config passed into the constructor
8402      */
8403     this.config = []; //config;
8404     this.lookup = {};
8405
8406     // if no id, create one
8407     // if the column does not have a dataIndex mapping,
8408     // map it to the order it is in the config
8409     for(var i = 0, len = config.length; i < len; i++){
8410         this.addColumn(config[i]);
8411         
8412     }
8413
8414     /**
8415      * The width of columns which have no width specified (defaults to 100)
8416      * @type Number
8417      */
8418     this.defaultWidth = 100;
8419
8420     /**
8421      * Default sortable of columns which have no sortable specified (defaults to false)
8422      * @type Boolean
8423      */
8424     this.defaultSortable = false;
8425
8426     this.addEvents({
8427         /**
8428              * @event widthchange
8429              * Fires when the width of a column changes.
8430              * @param {ColumnModel} this
8431              * @param {Number} columnIndex The column index
8432              * @param {Number} newWidth The new width
8433              */
8434             "widthchange": true,
8435         /**
8436              * @event headerchange
8437              * Fires when the text of a header changes.
8438              * @param {ColumnModel} this
8439              * @param {Number} columnIndex The column index
8440              * @param {Number} newText The new header text
8441              */
8442             "headerchange": true,
8443         /**
8444              * @event hiddenchange
8445              * Fires when a column is hidden or "unhidden".
8446              * @param {ColumnModel} this
8447              * @param {Number} columnIndex The column index
8448              * @param {Boolean} hidden true if hidden, false otherwise
8449              */
8450             "hiddenchange": true,
8451             /**
8452          * @event columnmoved
8453          * Fires when a column is moved.
8454          * @param {ColumnModel} this
8455          * @param {Number} oldIndex
8456          * @param {Number} newIndex
8457          */
8458         "columnmoved" : true,
8459         /**
8460          * @event columlockchange
8461          * Fires when a column's locked state is changed
8462          * @param {ColumnModel} this
8463          * @param {Number} colIndex
8464          * @param {Boolean} locked true if locked
8465          */
8466         "columnlockchange" : true
8467     });
8468     Roo.grid.ColumnModel.superclass.constructor.call(this);
8469 };
8470 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8471     /**
8472      * @cfg {String} header [required] The header text to display in the Grid view.
8473      */
8474         /**
8475      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8476      */
8477         /**
8478      * @cfg {String} smHeader Header at Bootsrap Small width
8479      */
8480         /**
8481      * @cfg {String} mdHeader Header at Bootsrap Medium width
8482      */
8483         /**
8484      * @cfg {String} lgHeader Header at Bootsrap Large width
8485      */
8486         /**
8487      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8488      */
8489     /**
8490      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8491      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8492      * specified, the column's index is used as an index into the Record's data Array.
8493      */
8494     /**
8495      * @cfg {Number} width  The initial width in pixels of the column. Using this
8496      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8497      */
8498     /**
8499      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8500      * Defaults to the value of the {@link #defaultSortable} property.
8501      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8502      */
8503     /**
8504      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8505      */
8506     /**
8507      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8508      */
8509     /**
8510      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8511      */
8512     /**
8513      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8514      */
8515     /**
8516      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8517      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8518      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8519      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8520      */
8521        /**
8522      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8523      */
8524     /**
8525      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8526      */
8527     /**
8528      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8529      */
8530     /**
8531      * @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)
8532      */
8533     /**
8534      * @cfg {String} tooltip mouse over tooltip text
8535      */
8536     /**
8537      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8541      */
8542     /**
8543      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8547      */
8548         /**
8549      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8550      */
8551     /**
8552      * Returns the id of the column at the specified index.
8553      * @param {Number} index The column index
8554      * @return {String} the id
8555      */
8556     getColumnId : function(index){
8557         return this.config[index].id;
8558     },
8559
8560     /**
8561      * Returns the column for a specified id.
8562      * @param {String} id The column id
8563      * @return {Object} the column
8564      */
8565     getColumnById : function(id){
8566         return this.lookup[id];
8567     },
8568
8569     
8570     /**
8571      * Returns the column Object for a specified dataIndex.
8572      * @param {String} dataIndex The column dataIndex
8573      * @return {Object|Boolean} the column or false if not found
8574      */
8575     getColumnByDataIndex: function(dataIndex){
8576         var index = this.findColumnIndex(dataIndex);
8577         return index > -1 ? this.config[index] : false;
8578     },
8579     
8580     /**
8581      * Returns the index for a specified column id.
8582      * @param {String} id The column id
8583      * @return {Number} the index, or -1 if not found
8584      */
8585     getIndexById : function(id){
8586         for(var i = 0, len = this.config.length; i < len; i++){
8587             if(this.config[i].id == id){
8588                 return i;
8589             }
8590         }
8591         return -1;
8592     },
8593     
8594     /**
8595      * Returns the index for a specified column dataIndex.
8596      * @param {String} dataIndex The column dataIndex
8597      * @return {Number} the index, or -1 if not found
8598      */
8599     
8600     findColumnIndex : function(dataIndex){
8601         for(var i = 0, len = this.config.length; i < len; i++){
8602             if(this.config[i].dataIndex == dataIndex){
8603                 return i;
8604             }
8605         }
8606         return -1;
8607     },
8608     
8609     
8610     moveColumn : function(oldIndex, newIndex){
8611         var c = this.config[oldIndex];
8612         this.config.splice(oldIndex, 1);
8613         this.config.splice(newIndex, 0, c);
8614         this.dataMap = null;
8615         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8616     },
8617
8618     isLocked : function(colIndex){
8619         return this.config[colIndex].locked === true;
8620     },
8621
8622     setLocked : function(colIndex, value, suppressEvent){
8623         if(this.isLocked(colIndex) == value){
8624             return;
8625         }
8626         this.config[colIndex].locked = value;
8627         if(!suppressEvent){
8628             this.fireEvent("columnlockchange", this, colIndex, value);
8629         }
8630     },
8631
8632     getTotalLockedWidth : function(){
8633         var totalWidth = 0;
8634         for(var i = 0; i < this.config.length; i++){
8635             if(this.isLocked(i) && !this.isHidden(i)){
8636                 this.totalWidth += this.getColumnWidth(i);
8637             }
8638         }
8639         return totalWidth;
8640     },
8641
8642     getLockedCount : function(){
8643         for(var i = 0, len = this.config.length; i < len; i++){
8644             if(!this.isLocked(i)){
8645                 return i;
8646             }
8647         }
8648         
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the number of columns.
8654      * @return {Number}
8655      */
8656     getColumnCount : function(visibleOnly){
8657         if(visibleOnly === true){
8658             var c = 0;
8659             for(var i = 0, len = this.config.length; i < len; i++){
8660                 if(!this.isHidden(i)){
8661                     c++;
8662                 }
8663             }
8664             return c;
8665         }
8666         return this.config.length;
8667     },
8668
8669     /**
8670      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8671      * @param {Function} fn
8672      * @param {Object} scope (optional)
8673      * @return {Array} result
8674      */
8675     getColumnsBy : function(fn, scope){
8676         var r = [];
8677         for(var i = 0, len = this.config.length; i < len; i++){
8678             var c = this.config[i];
8679             if(fn.call(scope||this, c, i) === true){
8680                 r[r.length] = c;
8681             }
8682         }
8683         return r;
8684     },
8685
8686     /**
8687      * Returns true if the specified column is sortable.
8688      * @param {Number} col The column index
8689      * @return {Boolean}
8690      */
8691     isSortable : function(col){
8692         if(typeof this.config[col].sortable == "undefined"){
8693             return this.defaultSortable;
8694         }
8695         return this.config[col].sortable;
8696     },
8697
8698     /**
8699      * Returns the rendering (formatting) function defined for the column.
8700      * @param {Number} col The column index.
8701      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8702      */
8703     getRenderer : function(col){
8704         if(!this.config[col].renderer){
8705             return Roo.grid.ColumnModel.defaultRenderer;
8706         }
8707         return this.config[col].renderer;
8708     },
8709
8710     /**
8711      * Sets the rendering (formatting) function for a column.
8712      * @param {Number} col The column index
8713      * @param {Function} fn The function to use to process the cell's raw data
8714      * to return HTML markup for the grid view. The render function is called with
8715      * the following parameters:<ul>
8716      * <li>Data value.</li>
8717      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8718      * <li>css A CSS style string to apply to the table cell.</li>
8719      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8720      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8721      * <li>Row index</li>
8722      * <li>Column index</li>
8723      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8724      */
8725     setRenderer : function(col, fn){
8726         this.config[col].renderer = fn;
8727     },
8728
8729     /**
8730      * Returns the width for the specified column.
8731      * @param {Number} col The column index
8732      * @param (optional) {String} gridSize bootstrap width size.
8733      * @return {Number}
8734      */
8735     getColumnWidth : function(col, gridSize)
8736         {
8737                 var cfg = this.config[col];
8738                 
8739                 if (typeof(gridSize) == 'undefined') {
8740                         return cfg.width * 1 || this.defaultWidth;
8741                 }
8742                 if (gridSize === false) { // if we set it..
8743                         return cfg.width || false;
8744                 }
8745                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8746                 
8747                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8748                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8749                                 continue;
8750                         }
8751                         return cfg[ sizes[i] ];
8752                 }
8753                 return 1;
8754                 
8755     },
8756
8757     /**
8758      * Sets the width for a column.
8759      * @param {Number} col The column index
8760      * @param {Number} width The new width
8761      */
8762     setColumnWidth : function(col, width, suppressEvent){
8763         this.config[col].width = width;
8764         this.totalWidth = null;
8765         if(!suppressEvent){
8766              this.fireEvent("widthchange", this, col, width);
8767         }
8768     },
8769
8770     /**
8771      * Returns the total width of all columns.
8772      * @param {Boolean} includeHidden True to include hidden column widths
8773      * @return {Number}
8774      */
8775     getTotalWidth : function(includeHidden){
8776         if(!this.totalWidth){
8777             this.totalWidth = 0;
8778             for(var i = 0, len = this.config.length; i < len; i++){
8779                 if(includeHidden || !this.isHidden(i)){
8780                     this.totalWidth += this.getColumnWidth(i);
8781                 }
8782             }
8783         }
8784         return this.totalWidth;
8785     },
8786
8787     /**
8788      * Returns the header for the specified column.
8789      * @param {Number} col The column index
8790      * @return {String}
8791      */
8792     getColumnHeader : function(col){
8793         return this.config[col].header;
8794     },
8795
8796     /**
8797      * Sets the header for a column.
8798      * @param {Number} col The column index
8799      * @param {String} header The new header
8800      */
8801     setColumnHeader : function(col, header){
8802         this.config[col].header = header;
8803         this.fireEvent("headerchange", this, col, header);
8804     },
8805
8806     /**
8807      * Returns the tooltip for the specified column.
8808      * @param {Number} col The column index
8809      * @return {String}
8810      */
8811     getColumnTooltip : function(col){
8812             return this.config[col].tooltip;
8813     },
8814     /**
8815      * Sets the tooltip for a column.
8816      * @param {Number} col The column index
8817      * @param {String} tooltip The new tooltip
8818      */
8819     setColumnTooltip : function(col, tooltip){
8820             this.config[col].tooltip = tooltip;
8821     },
8822
8823     /**
8824      * Returns the dataIndex for the specified column.
8825      * @param {Number} col The column index
8826      * @return {Number}
8827      */
8828     getDataIndex : function(col){
8829         return this.config[col].dataIndex;
8830     },
8831
8832     /**
8833      * Sets the dataIndex for a column.
8834      * @param {Number} col The column index
8835      * @param {Number} dataIndex The new dataIndex
8836      */
8837     setDataIndex : function(col, dataIndex){
8838         this.config[col].dataIndex = dataIndex;
8839     },
8840
8841     
8842     
8843     /**
8844      * Returns true if the cell is editable.
8845      * @param {Number} colIndex The column index
8846      * @param {Number} rowIndex The row index - this is nto actually used..?
8847      * @return {Boolean}
8848      */
8849     isCellEditable : function(colIndex, rowIndex){
8850         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8851     },
8852
8853     /**
8854      * Returns the editor defined for the cell/column.
8855      * return false or null to disable editing.
8856      * @param {Number} colIndex The column index
8857      * @param {Number} rowIndex The row index
8858      * @return {Object}
8859      */
8860     getCellEditor : function(colIndex, rowIndex){
8861         return this.config[colIndex].editor;
8862     },
8863
8864     /**
8865      * Sets if a column is editable.
8866      * @param {Number} col The column index
8867      * @param {Boolean} editable True if the column is editable
8868      */
8869     setEditable : function(col, editable){
8870         this.config[col].editable = editable;
8871     },
8872
8873
8874     /**
8875      * Returns true if the column is hidden.
8876      * @param {Number} colIndex The column index
8877      * @return {Boolean}
8878      */
8879     isHidden : function(colIndex){
8880         return this.config[colIndex].hidden;
8881     },
8882
8883
8884     /**
8885      * Returns true if the column width cannot be changed
8886      */
8887     isFixed : function(colIndex){
8888         return this.config[colIndex].fixed;
8889     },
8890
8891     /**
8892      * Returns true if the column can be resized
8893      * @return {Boolean}
8894      */
8895     isResizable : function(colIndex){
8896         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8897     },
8898     /**
8899      * Sets if a column is hidden.
8900      * @param {Number} colIndex The column index
8901      * @param {Boolean} hidden True if the column is hidden
8902      */
8903     setHidden : function(colIndex, hidden){
8904         this.config[colIndex].hidden = hidden;
8905         this.totalWidth = null;
8906         this.fireEvent("hiddenchange", this, colIndex, hidden);
8907     },
8908
8909     /**
8910      * Sets the editor for a column.
8911      * @param {Number} col The column index
8912      * @param {Object} editor The editor object
8913      */
8914     setEditor : function(col, editor){
8915         this.config[col].editor = editor;
8916     },
8917     /**
8918      * Add a column (experimental...) - defaults to adding to the end..
8919      * @param {Object} config 
8920     */
8921     addColumn : function(c)
8922     {
8923     
8924         var i = this.config.length;
8925         this.config[i] = c;
8926         
8927         if(typeof c.dataIndex == "undefined"){
8928             c.dataIndex = i;
8929         }
8930         if(typeof c.renderer == "string"){
8931             c.renderer = Roo.util.Format[c.renderer];
8932         }
8933         if(typeof c.id == "undefined"){
8934             c.id = Roo.id();
8935         }
8936         if(c.editor && c.editor.xtype){
8937             c.editor  = Roo.factory(c.editor, Roo.grid);
8938         }
8939         if(c.editor && c.editor.isFormField){
8940             c.editor = new Roo.grid.GridEditor(c.editor);
8941         }
8942         this.lookup[c.id] = c;
8943     }
8944     
8945 });
8946
8947 Roo.grid.ColumnModel.defaultRenderer = function(value)
8948 {
8949     if(typeof value == "object") {
8950         return value;
8951     }
8952         if(typeof value == "string" && value.length < 1){
8953             return "&#160;";
8954         }
8955     
8956         return String.format("{0}", value);
8957 };
8958
8959 // Alias for backwards compatibility
8960 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8961 /*
8962  * Based on:
8963  * Ext JS Library 1.1.1
8964  * Copyright(c) 2006-2007, Ext JS, LLC.
8965  *
8966  * Originally Released Under LGPL - original licence link has changed is not relivant.
8967  *
8968  * Fork - LGPL
8969  * <script type="text/javascript">
8970  */
8971  
8972 /**
8973  * @class Roo.LoadMask
8974  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8975  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8976  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8977  * element's UpdateManager load indicator and will be destroyed after the initial load.
8978  * @constructor
8979  * Create a new LoadMask
8980  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8981  * @param {Object} config The config object
8982  */
8983 Roo.LoadMask = function(el, config){
8984     this.el = Roo.get(el);
8985     Roo.apply(this, config);
8986     if(this.store){
8987         this.store.on('beforeload', this.onBeforeLoad, this);
8988         this.store.on('load', this.onLoad, this);
8989         this.store.on('loadexception', this.onLoadException, this);
8990         this.removeMask = false;
8991     }else{
8992         var um = this.el.getUpdateManager();
8993         um.showLoadIndicator = false; // disable the default indicator
8994         um.on('beforeupdate', this.onBeforeLoad, this);
8995         um.on('update', this.onLoad, this);
8996         um.on('failure', this.onLoad, this);
8997         this.removeMask = true;
8998     }
8999 };
9000
9001 Roo.LoadMask.prototype = {
9002     /**
9003      * @cfg {Boolean} removeMask
9004      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9005      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9006      */
9007     removeMask : false,
9008     /**
9009      * @cfg {String} msg
9010      * The text to display in a centered loading message box (defaults to 'Loading...')
9011      */
9012     msg : 'Loading...',
9013     /**
9014      * @cfg {String} msgCls
9015      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9016      */
9017     msgCls : 'x-mask-loading',
9018
9019     /**
9020      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9021      * @type Boolean
9022      */
9023     disabled: false,
9024
9025     /**
9026      * Disables the mask to prevent it from being displayed
9027      */
9028     disable : function(){
9029        this.disabled = true;
9030     },
9031
9032     /**
9033      * Enables the mask so that it can be displayed
9034      */
9035     enable : function(){
9036         this.disabled = false;
9037     },
9038     
9039     onLoadException : function()
9040     {
9041         Roo.log(arguments);
9042         
9043         if (typeof(arguments[3]) != 'undefined') {
9044             Roo.MessageBox.alert("Error loading",arguments[3]);
9045         } 
9046         /*
9047         try {
9048             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9049                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9050             }   
9051         } catch(e) {
9052             
9053         }
9054         */
9055     
9056         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9057     },
9058     // private
9059     onLoad : function()
9060     {
9061         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9062     },
9063
9064     // private
9065     onBeforeLoad : function(){
9066         if(!this.disabled){
9067             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9068         }
9069     },
9070
9071     // private
9072     destroy : function(){
9073         if(this.store){
9074             this.store.un('beforeload', this.onBeforeLoad, this);
9075             this.store.un('load', this.onLoad, this);
9076             this.store.un('loadexception', this.onLoadException, this);
9077         }else{
9078             var um = this.el.getUpdateManager();
9079             um.un('beforeupdate', this.onBeforeLoad, this);
9080             um.un('update', this.onLoad, this);
9081             um.un('failure', this.onLoad, this);
9082         }
9083     }
9084 };/**
9085  * @class Roo.bootstrap.Table
9086  * @licence LGBL
9087  * @extends Roo.bootstrap.Component
9088  * @children Roo.bootstrap.TableBody
9089  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9090  * Similar to Roo.grid.Grid
9091  * <pre><code>
9092  var table = Roo.factory({
9093     xtype : 'Table',
9094     xns : Roo.bootstrap,
9095     autoSizeColumns: true,
9096     
9097     
9098     store : {
9099         xtype : 'Store',
9100         xns : Roo.data,
9101         remoteSort : true,
9102         sortInfo : { direction : 'ASC', field: 'name' },
9103         proxy : {
9104            xtype : 'HttpProxy',
9105            xns : Roo.data,
9106            method : 'GET',
9107            url : 'https://example.com/some.data.url.json'
9108         },
9109         reader : {
9110            xtype : 'JsonReader',
9111            xns : Roo.data,
9112            fields : [ 'id', 'name', whatever' ],
9113            id : 'id',
9114            root : 'data'
9115         }
9116     },
9117     cm : [
9118         {
9119             xtype : 'ColumnModel',
9120             xns : Roo.grid,
9121             align : 'center',
9122             cursor : 'pointer',
9123             dataIndex : 'is_in_group',
9124             header : "Name",
9125             sortable : true,
9126             renderer : function(v, x , r) {  
9127             
9128                 return String.format("{0}", v)
9129             }
9130             width : 3
9131         } // more columns..
9132     ],
9133     selModel : {
9134         xtype : 'RowSelectionModel',
9135         xns : Roo.bootstrap.Table
9136         // you can add listeners to catch selection change here....
9137     }
9138      
9139
9140  });
9141  // set any options
9142  grid.render(Roo.get("some-div"));
9143 </code></pre>
9144
9145 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9146
9147
9148
9149  *
9150  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9151  * @cfg {Roo.data.Store} store The data store to use
9152  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9153  * 
9154  * @cfg {String} cls table class
9155  *
9156  *
9157  * @cfg {string} empty_results  Text to display for no results 
9158  * @cfg {boolean} striped Should the rows be alternative striped
9159  * @cfg {boolean} bordered Add borders to the table
9160  * @cfg {boolean} hover Add hover highlighting
9161  * @cfg {boolean} condensed Format condensed
9162  * @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,
9163  *                also adds table-responsive (see bootstrap docs for details)
9164  * @cfg {Boolean} loadMask (true|false) default false
9165  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9166  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9167  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9168  * @cfg {Boolean} rowSelection (true|false) default false
9169  * @cfg {Boolean} cellSelection (true|false) default false
9170  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9171  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9172  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9173  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9174  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9175  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9176  *
9177  * 
9178  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9179  * 
9180  * @constructor
9181  * Create a new Table
9182  * @param {Object} config The config object
9183  */
9184
9185 Roo.bootstrap.Table = function(config)
9186 {
9187     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9188      
9189     // BC...
9190     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9191     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9192     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9193     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9194     
9195     this.view = this; // compat with grid.
9196     
9197     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9198     if (this.sm) {
9199         this.sm.grid = this;
9200         this.selModel = Roo.factory(this.sm, Roo.grid);
9201         this.sm = this.selModel;
9202         this.sm.xmodule = this.xmodule || false;
9203     }
9204     
9205     if (this.cm && typeof(this.cm.config) == 'undefined') {
9206         this.colModel = new Roo.grid.ColumnModel(this.cm);
9207         this.cm = this.colModel;
9208         this.cm.xmodule = this.xmodule || false;
9209     }
9210     if (this.store) {
9211         this.store= Roo.factory(this.store, Roo.data);
9212         this.ds = this.store;
9213         this.ds.xmodule = this.xmodule || false;
9214          
9215     }
9216     if (this.footer && this.store) {
9217         this.footer.dataSource = this.ds;
9218         this.footer = Roo.factory(this.footer);
9219     }
9220     
9221     /** @private */
9222     this.addEvents({
9223         /**
9224          * @event cellclick
9225          * Fires when a cell is clicked
9226          * @param {Roo.bootstrap.Table} this
9227          * @param {Roo.Element} el
9228          * @param {Number} rowIndex
9229          * @param {Number} columnIndex
9230          * @param {Roo.EventObject} e
9231          */
9232         "cellclick" : true,
9233         /**
9234          * @event celldblclick
9235          * Fires when a cell is double clicked
9236          * @param {Roo.bootstrap.Table} this
9237          * @param {Roo.Element} el
9238          * @param {Number} rowIndex
9239          * @param {Number} columnIndex
9240          * @param {Roo.EventObject} e
9241          */
9242         "celldblclick" : true,
9243         /**
9244          * @event rowclick
9245          * Fires when a row is clicked
9246          * @param {Roo.bootstrap.Table} this
9247          * @param {Roo.Element} el
9248          * @param {Number} rowIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "rowclick" : true,
9252         /**
9253          * @event rowdblclick
9254          * Fires when a row is double clicked
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Roo.EventObject} e
9259          */
9260         "rowdblclick" : true,
9261         /**
9262          * @event mouseover
9263          * Fires when a mouseover occur
9264          * @param {Roo.bootstrap.Table} this
9265          * @param {Roo.Element} el
9266          * @param {Number} rowIndex
9267          * @param {Number} columnIndex
9268          * @param {Roo.EventObject} e
9269          */
9270         "mouseover" : true,
9271         /**
9272          * @event mouseout
9273          * Fires when a mouseout occur
9274          * @param {Roo.bootstrap.Table} this
9275          * @param {Roo.Element} el
9276          * @param {Number} rowIndex
9277          * @param {Number} columnIndex
9278          * @param {Roo.EventObject} e
9279          */
9280         "mouseout" : true,
9281         /**
9282          * @event rowclass
9283          * Fires when a row is rendered, so you can change add a style to it.
9284          * @param {Roo.bootstrap.Table} this
9285          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9286          */
9287         'rowclass' : true,
9288           /**
9289          * @event rowsrendered
9290          * Fires when all the  rows have been rendered
9291          * @param {Roo.bootstrap.Table} this
9292          */
9293         'rowsrendered' : true,
9294         /**
9295          * @event contextmenu
9296          * The raw contextmenu event for the entire grid.
9297          * @param {Roo.EventObject} e
9298          */
9299         "contextmenu" : true,
9300         /**
9301          * @event rowcontextmenu
9302          * Fires when a row is right clicked
9303          * @param {Roo.bootstrap.Table} this
9304          * @param {Number} rowIndex
9305          * @param {Roo.EventObject} e
9306          */
9307         "rowcontextmenu" : true,
9308         /**
9309          * @event cellcontextmenu
9310          * Fires when a cell is right clicked
9311          * @param {Roo.bootstrap.Table} this
9312          * @param {Number} rowIndex
9313          * @param {Number} cellIndex
9314          * @param {Roo.EventObject} e
9315          */
9316          "cellcontextmenu" : true,
9317          /**
9318          * @event headercontextmenu
9319          * Fires when a header is right clicked
9320          * @param {Roo.bootstrap.Table} this
9321          * @param {Number} columnIndex
9322          * @param {Roo.EventObject} e
9323          */
9324         "headercontextmenu" : true,
9325         /**
9326          * @event mousedown
9327          * The raw mousedown event for the entire grid.
9328          * @param {Roo.EventObject} e
9329          */
9330         "mousedown" : true
9331         
9332     });
9333 };
9334
9335 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9336     
9337     cls: false,
9338     
9339     empty_results : '',
9340     striped : false,
9341     scrollBody : false,
9342     bordered: false,
9343     hover:  false,
9344     condensed : false,
9345     responsive : false,
9346     sm : false,
9347     cm : false,
9348     store : false,
9349     loadMask : false,
9350     footerShow : true,
9351     footerRow : false,
9352     headerShow : true,
9353     enableColumnResize: true,
9354     disableAutoSize: false,
9355   
9356     rowSelection : false,
9357     cellSelection : false,
9358     layout : false,
9359
9360     minColumnWidth : 50,
9361     
9362     // Roo.Element - the tbody
9363     bodyEl: false,  // <tbody> Roo.Element - thead element    
9364     headEl: false,  // <thead> Roo.Element - thead element
9365     resizeProxy : false, // proxy element for dragging?
9366
9367
9368     
9369     container: false, // used by gridpanel...
9370     
9371     lazyLoad : false,
9372     
9373     CSS : Roo.util.CSS,
9374     
9375     auto_hide_footer : false,
9376     
9377     view: false, // actually points to this..
9378     
9379     getAutoCreate : function()
9380     {
9381         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9382         
9383         cfg = {
9384             tag: 'table',
9385             cls : 'table', 
9386             cn : []
9387         };
9388         // this get's auto added by panel.Grid
9389         if (this.scrollBody) {
9390             cfg.cls += ' table-body-fixed';
9391         }    
9392         if (this.striped) {
9393             cfg.cls += ' table-striped';
9394         }
9395         
9396         if (this.hover) {
9397             cfg.cls += ' table-hover';
9398         }
9399         if (this.bordered) {
9400             cfg.cls += ' table-bordered';
9401         }
9402         if (this.condensed) {
9403             cfg.cls += ' table-condensed';
9404         }
9405         
9406         if (this.responsive) {
9407             cfg.cls += ' table-responsive';
9408         }
9409         
9410         if (this.cls) {
9411             cfg.cls+=  ' ' +this.cls;
9412         }
9413         
9414         
9415         
9416         if (this.layout) {
9417             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9418         }
9419         
9420         if(this.store || this.cm){
9421             if(this.headerShow){
9422                 cfg.cn.push(this.renderHeader());
9423             }
9424             
9425             cfg.cn.push(this.renderBody());
9426             
9427             if(this.footerShow || this.footerRow){
9428                 cfg.cn.push(this.renderFooter());
9429             }
9430
9431             // where does this come from?
9432             //cfg.cls+=  ' TableGrid';
9433         }
9434         
9435         return { cn : [ cfg ] };
9436     },
9437     
9438     initEvents : function()
9439     {   
9440         if(!this.store || !this.cm){
9441             return;
9442         }
9443         if (this.selModel) {
9444             this.selModel.initEvents();
9445         }
9446         
9447         
9448         //Roo.log('initEvents with ds!!!!');
9449         
9450         this.bodyEl = this.el.select('tbody', true).first();
9451         this.headEl = this.el.select('thead', true).first();
9452         this.mainFoot = this.el.select('tfoot', true).first();
9453         
9454         
9455         
9456         
9457         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9458             e.on('click', this.sort, this);
9459         }, this);
9460         
9461         
9462         // why is this done????? = it breaks dialogs??
9463         //this.parent().el.setStyle('position', 'relative');
9464         
9465         
9466         if (this.footer) {
9467             this.footer.parentId = this.id;
9468             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9469             
9470             if(this.lazyLoad){
9471                 this.el.select('tfoot tr td').first().addClass('hide');
9472             }
9473         } 
9474         
9475         if(this.loadMask) {
9476             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9477         }
9478         
9479         this.store.on('load', this.onLoad, this);
9480         this.store.on('beforeload', this.onBeforeLoad, this);
9481         this.store.on('update', this.onUpdate, this);
9482         this.store.on('add', this.onAdd, this);
9483         this.store.on("clear", this.clear, this);
9484         
9485         this.el.on("contextmenu", this.onContextMenu, this);
9486         
9487         
9488         this.cm.on("headerchange", this.onHeaderChange, this);
9489         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9490
9491  //?? does bodyEl get replaced on render?
9492         this.bodyEl.on("click", this.onClick, this);
9493         this.bodyEl.on("dblclick", this.onDblClick, this);        
9494         this.bodyEl.on('scroll', this.onBodyScroll, this);
9495
9496         // guessing mainbody will work - this relays usually caught by selmodel at present.
9497         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9498   
9499   
9500         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9501         
9502   
9503         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9504             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9505         }
9506         
9507         this.initCSS();
9508     },
9509     // Compatibility with grid - we implement all the view features at present.
9510     getView : function()
9511     {
9512         return this;
9513     },
9514     
9515     initCSS : function()
9516     {
9517         if(this.disableAutoSize) {
9518             return;
9519         }
9520         
9521         var cm = this.cm, styles = [];
9522         this.CSS.removeStyleSheet(this.id + '-cssrules');
9523         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9524         // we can honour xs/sm/md/xl  as widths...
9525         // we first have to decide what widht we are currently at...
9526         var sz = Roo.getGridSize();
9527         
9528         var total = 0;
9529         var last = -1;
9530         var cols = []; // visable cols.
9531         var total_abs = 0;
9532         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9533             var w = cm.getColumnWidth(i, false);
9534             if(cm.isHidden(i)){
9535                 cols.push( { rel : false, abs : 0 });
9536                 continue;
9537             }
9538             if (w !== false) {
9539                 cols.push( { rel : false, abs : w });
9540                 total_abs += w;
9541                 last = i; // not really..
9542                 continue;
9543             }
9544             var w = cm.getColumnWidth(i, sz);
9545             if (w > 0) {
9546                 last = i
9547             }
9548             total += w;
9549             cols.push( { rel : w, abs : false });
9550         }
9551         
9552         var avail = this.bodyEl.dom.clientWidth - total_abs;
9553         
9554         var unitWidth = Math.floor(avail / total);
9555         var rem = avail - (unitWidth * total);
9556         
9557         var hidden, width, pos = 0 , splithide , left;
9558         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9559             
9560             hidden = 'display:none;';
9561             left = '';
9562             width  = 'width:0px;';
9563             splithide = '';
9564             if(!cm.isHidden(i)){
9565                 hidden = '';
9566                 
9567                 
9568                 // we can honour xs/sm/md/xl ?
9569                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9570                 if (w===0) {
9571                     hidden = 'display:none;';
9572                 }
9573                 // width should return a small number...
9574                 if (i == last) {
9575                     w+=rem; // add the remaining with..
9576                 }
9577                 pos += w;
9578                 left = "left:" + (pos -4) + "px;";
9579                 width = "width:" + w+ "px;";
9580                 
9581             }
9582             if (this.responsive) {
9583                 width = '';
9584                 left = '';
9585                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9586                 splithide = 'display: none;';
9587             }
9588             
9589             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9590             if (this.headEl) {
9591                 if (i == last) {
9592                     splithide = 'display:none;';
9593                 }
9594                 
9595                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9596                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9597                             // this is the popover version..
9598                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9599                 );
9600             }
9601             
9602         }
9603         //Roo.log(styles.join(''));
9604         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9605         
9606     },
9607     
9608     
9609     
9610     onContextMenu : function(e, t)
9611     {
9612         this.processEvent("contextmenu", e);
9613     },
9614     
9615     processEvent : function(name, e)
9616     {
9617         if (name != 'touchstart' ) {
9618             this.fireEvent(name, e);    
9619         }
9620         
9621         var t = e.getTarget();
9622         
9623         var cell = Roo.get(t);
9624         
9625         if(!cell){
9626             return;
9627         }
9628         
9629         if(cell.findParent('tfoot', false, true)){
9630             return;
9631         }
9632         
9633         if(cell.findParent('thead', false, true)){
9634             
9635             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9636                 cell = Roo.get(t).findParent('th', false, true);
9637                 if (!cell) {
9638                     Roo.log("failed to find th in thead?");
9639                     Roo.log(e.getTarget());
9640                     return;
9641                 }
9642             }
9643             
9644             var cellIndex = cell.dom.cellIndex;
9645             
9646             var ename = name == 'touchstart' ? 'click' : name;
9647             this.fireEvent("header" + ename, this, cellIndex, e);
9648             
9649             return;
9650         }
9651         
9652         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9653             cell = Roo.get(t).findParent('td', false, true);
9654             if (!cell) {
9655                 Roo.log("failed to find th in tbody?");
9656                 Roo.log(e.getTarget());
9657                 return;
9658             }
9659         }
9660         
9661         var row = cell.findParent('tr', false, true);
9662         var cellIndex = cell.dom.cellIndex;
9663         var rowIndex = row.dom.rowIndex - 1;
9664         
9665         if(row !== false){
9666             
9667             this.fireEvent("row" + name, this, rowIndex, e);
9668             
9669             if(cell !== false){
9670             
9671                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9672             }
9673         }
9674         
9675     },
9676     
9677     onMouseover : function(e, el)
9678     {
9679         var cell = Roo.get(el);
9680         
9681         if(!cell){
9682             return;
9683         }
9684         
9685         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9686             cell = cell.findParent('td', false, true);
9687         }
9688         
9689         var row = cell.findParent('tr', false, true);
9690         var cellIndex = cell.dom.cellIndex;
9691         var rowIndex = row.dom.rowIndex - 1; // start from 0
9692         
9693         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9694         
9695     },
9696     
9697     onMouseout : function(e, el)
9698     {
9699         var cell = Roo.get(el);
9700         
9701         if(!cell){
9702             return;
9703         }
9704         
9705         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9706             cell = cell.findParent('td', false, true);
9707         }
9708         
9709         var row = cell.findParent('tr', false, true);
9710         var cellIndex = cell.dom.cellIndex;
9711         var rowIndex = row.dom.rowIndex - 1; // start from 0
9712         
9713         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9714         
9715     },
9716     
9717     onClick : function(e, el)
9718     {
9719         var cell = Roo.get(el);
9720         
9721         if(!cell || (!this.cellSelection && !this.rowSelection)){
9722             return;
9723         }
9724         
9725         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9726             cell = cell.findParent('td', false, true);
9727         }
9728         
9729         if(!cell || typeof(cell) == 'undefined'){
9730             return;
9731         }
9732         
9733         var row = cell.findParent('tr', false, true);
9734         
9735         if(!row || typeof(row) == 'undefined'){
9736             return;
9737         }
9738         
9739         var cellIndex = cell.dom.cellIndex;
9740         var rowIndex = this.getRowIndex(row);
9741         
9742         // why??? - should these not be based on SelectionModel?
9743         //if(this.cellSelection){
9744             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9745         //}
9746         
9747         //if(this.rowSelection){
9748             this.fireEvent('rowclick', this, row, rowIndex, e);
9749         //}
9750          
9751     },
9752         
9753     onDblClick : function(e,el)
9754     {
9755         var cell = Roo.get(el);
9756         
9757         if(!cell || (!this.cellSelection && !this.rowSelection)){
9758             return;
9759         }
9760         
9761         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9762             cell = cell.findParent('td', false, true);
9763         }
9764         
9765         if(!cell || typeof(cell) == 'undefined'){
9766             return;
9767         }
9768         
9769         var row = cell.findParent('tr', false, true);
9770         
9771         if(!row || typeof(row) == 'undefined'){
9772             return;
9773         }
9774         
9775         var cellIndex = cell.dom.cellIndex;
9776         var rowIndex = this.getRowIndex(row);
9777         
9778         if(this.cellSelection){
9779             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9780         }
9781         
9782         if(this.rowSelection){
9783             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9784         }
9785     },
9786     findRowIndex : function(el)
9787     {
9788         var cell = Roo.get(el);
9789         if(!cell) {
9790             return false;
9791         }
9792         var row = cell.findParent('tr', false, true);
9793         
9794         if(!row || typeof(row) == 'undefined'){
9795             return false;
9796         }
9797         return this.getRowIndex(row);
9798     },
9799     sort : function(e,el)
9800     {
9801         var col = Roo.get(el);
9802         
9803         if(!col.hasClass('sortable')){
9804             return;
9805         }
9806         
9807         var sort = col.attr('sort');
9808         var dir = 'ASC';
9809         
9810         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9811             dir = 'DESC';
9812         }
9813         
9814         this.store.sortInfo = {field : sort, direction : dir};
9815         
9816         if (this.footer) {
9817             Roo.log("calling footer first");
9818             this.footer.onClick('first');
9819         } else {
9820         
9821             this.store.load({ params : { start : 0 } });
9822         }
9823     },
9824     
9825     renderHeader : function()
9826     {
9827         var header = {
9828             tag: 'thead',
9829             cn : []
9830         };
9831         
9832         var cm = this.cm;
9833         this.totalWidth = 0;
9834         
9835         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9836             
9837             var config = cm.config[i];
9838             
9839             var c = {
9840                 tag: 'th',
9841                 cls : 'x-hcol-' + i,
9842                 style : '',
9843                 
9844                 html: cm.getColumnHeader(i)
9845             };
9846             
9847             var tooltip = cm.getColumnTooltip(i);
9848             if (tooltip) {
9849                 c.tooltip = tooltip;
9850             }
9851             
9852             
9853             var hh = '';
9854             
9855             if(typeof(config.sortable) != 'undefined' && config.sortable){
9856                 c.cls += ' sortable';
9857                 c.html = '<i class="fa"></i>' + c.html;
9858             }
9859             
9860             // could use BS4 hidden-..-down 
9861             
9862             if(typeof(config.lgHeader) != 'undefined'){
9863                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9864             }
9865             
9866             if(typeof(config.mdHeader) != 'undefined'){
9867                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9868             }
9869             
9870             if(typeof(config.smHeader) != 'undefined'){
9871                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9872             }
9873             
9874             if(typeof(config.xsHeader) != 'undefined'){
9875                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9876             }
9877             
9878             if(hh.length){
9879                 c.html = hh;
9880             }
9881             
9882             if(typeof(config.tooltip) != 'undefined'){
9883                 c.tooltip = config.tooltip;
9884             }
9885             
9886             if(typeof(config.colspan) != 'undefined'){
9887                 c.colspan = config.colspan;
9888             }
9889             
9890             // hidden is handled by CSS now
9891             
9892             if(typeof(config.dataIndex) != 'undefined'){
9893                 c.sort = config.dataIndex;
9894             }
9895             
9896            
9897             
9898             if(typeof(config.align) != 'undefined' && config.align.length){
9899                 c.style += ' text-align:' + config.align + ';';
9900             }
9901             
9902             /* width is done in CSS
9903              *if(typeof(config.width) != 'undefined'){
9904                 c.style += ' width:' + config.width + 'px;';
9905                 this.totalWidth += config.width;
9906             } else {
9907                 this.totalWidth += 100; // assume minimum of 100 per column?
9908             }
9909             */
9910             
9911             if(typeof(config.cls) != 'undefined'){
9912                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9913             }
9914             // this is the bit that doesnt reall work at all...
9915             
9916             if (this.responsive) {
9917                  
9918             
9919                 ['xs','sm','md','lg'].map(function(size){
9920                     
9921                     if(typeof(config[size]) == 'undefined'){
9922                         return;
9923                     }
9924                      
9925                     if (!config[size]) { // 0 = hidden
9926                         // BS 4 '0' is treated as hide that column and below.
9927                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9928                         return;
9929                     }
9930                     
9931                     c.cls += ' col-' + size + '-' + config[size] + (
9932                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9933                     );
9934                     
9935                     
9936                 });
9937             }
9938             // at the end?
9939             
9940             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9941             
9942             
9943             
9944             
9945             header.cn.push(c)
9946         }
9947         
9948         return header;
9949     },
9950     
9951     renderBody : function()
9952     {
9953         var body = {
9954             tag: 'tbody',
9955             cn : [
9956                 {
9957                     tag: 'tr',
9958                     cn : [
9959                         {
9960                             tag : 'td',
9961                             colspan :  this.cm.getColumnCount()
9962                         }
9963                     ]
9964                 }
9965             ]
9966         };
9967         
9968         return body;
9969     },
9970     
9971     renderFooter : function()
9972     {
9973         var footer = {
9974             tag: 'tfoot',
9975             cn : [
9976                 {
9977                     tag: 'tr',
9978                     cn : [
9979                         {
9980                             tag : 'td',
9981                             colspan :  this.cm.getColumnCount()
9982                         }
9983                     ]
9984                 }
9985             ]
9986         };
9987         
9988         return footer;
9989     },
9990     
9991     onLoad : function()
9992     {
9993 //        Roo.log('ds onload');
9994         this.clear();
9995         
9996         var _this = this;
9997         var cm = this.cm;
9998         var ds = this.store;
9999         
10000         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10001             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10002             if (_this.store.sortInfo) {
10003                     
10004                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10005                     e.select('i', true).addClass(['fa-arrow-up']);
10006                 }
10007                 
10008                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10009                     e.select('i', true).addClass(['fa-arrow-down']);
10010                 }
10011             }
10012         });
10013         
10014         var tbody =  this.bodyEl;
10015               
10016         if(ds.getCount() > 0){
10017             ds.data.each(function(d,rowIndex){
10018                 var row =  this.renderRow(cm, ds, rowIndex);
10019                 
10020                 tbody.createChild(row);
10021                 
10022                 var _this = this;
10023                 
10024                 if(row.cellObjects.length){
10025                     Roo.each(row.cellObjects, function(r){
10026                         _this.renderCellObject(r);
10027                     })
10028                 }
10029                 
10030             }, this);
10031         } else if (this.empty_results.length) {
10032             this.el.mask(this.empty_results, 'no-spinner');
10033         }
10034         
10035         var tfoot = this.el.select('tfoot', true).first();
10036         
10037         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10038             
10039             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10040             
10041             var total = this.ds.getTotalCount();
10042             
10043             if(this.footer.pageSize < total){
10044                 this.mainFoot.show();
10045             }
10046         }
10047
10048         if(!this.footerShow && this.footerRow) {
10049
10050             var tr = {
10051                 tag : 'tr',
10052                 cn : []
10053             };
10054
10055             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10056                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10057                 var td = {
10058                     tag: 'td',
10059                     cls : ' x-fcol-' + i,
10060                     html: footer
10061                 };
10062
10063                 tr.cn.push(td);
10064                 
10065             }
10066             
10067             tfoot.dom.innerHTML = '';
10068
10069             tfoot.createChild(tr);
10070         }
10071         
10072         Roo.each(this.el.select('tbody td', true).elements, function(e){
10073             e.on('mouseover', _this.onMouseover, _this);
10074         });
10075         
10076         Roo.each(this.el.select('tbody td', true).elements, function(e){
10077             e.on('mouseout', _this.onMouseout, _this);
10078         });
10079         this.fireEvent('rowsrendered', this);
10080         
10081         this.autoSize();
10082         
10083         this.initCSS(); /// resize cols
10084
10085         
10086     },
10087     
10088     
10089     onUpdate : function(ds,record)
10090     {
10091         this.refreshRow(record);
10092         this.autoSize();
10093     },
10094     
10095     onRemove : function(ds, record, index, isUpdate){
10096         if(isUpdate !== true){
10097             this.fireEvent("beforerowremoved", this, index, record);
10098         }
10099         var bt = this.bodyEl.dom;
10100         
10101         var rows = this.el.select('tbody > tr', true).elements;
10102         
10103         if(typeof(rows[index]) != 'undefined'){
10104             bt.removeChild(rows[index].dom);
10105         }
10106         
10107 //        if(bt.rows[index]){
10108 //            bt.removeChild(bt.rows[index]);
10109 //        }
10110         
10111         if(isUpdate !== true){
10112             //this.stripeRows(index);
10113             //this.syncRowHeights(index, index);
10114             //this.layout();
10115             this.fireEvent("rowremoved", this, index, record);
10116         }
10117     },
10118     
10119     onAdd : function(ds, records, rowIndex)
10120     {
10121         //Roo.log('on Add called');
10122         // - note this does not handle multiple adding very well..
10123         var bt = this.bodyEl.dom;
10124         for (var i =0 ; i < records.length;i++) {
10125             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10126             //Roo.log(records[i]);
10127             //Roo.log(this.store.getAt(rowIndex+i));
10128             this.insertRow(this.store, rowIndex + i, false);
10129             return;
10130         }
10131         
10132     },
10133     
10134     
10135     refreshRow : function(record){
10136         var ds = this.store, index;
10137         if(typeof record == 'number'){
10138             index = record;
10139             record = ds.getAt(index);
10140         }else{
10141             index = ds.indexOf(record);
10142             if (index < 0) {
10143                 return; // should not happen - but seems to 
10144             }
10145         }
10146         this.insertRow(ds, index, true);
10147         this.autoSize();
10148         this.onRemove(ds, record, index+1, true);
10149         this.autoSize();
10150         //this.syncRowHeights(index, index);
10151         //this.layout();
10152         this.fireEvent("rowupdated", this, index, record);
10153     },
10154     // private - called by RowSelection
10155     onRowSelect : function(rowIndex){
10156         var row = this.getRowDom(rowIndex);
10157         row.addClass(['bg-info','info']);
10158     },
10159     // private - called by RowSelection
10160     onRowDeselect : function(rowIndex)
10161     {
10162         if (rowIndex < 0) {
10163             return;
10164         }
10165         var row = this.getRowDom(rowIndex);
10166         row.removeClass(['bg-info','info']);
10167     },
10168       /**
10169      * Focuses the specified row.
10170      * @param {Number} row The row index
10171      */
10172     focusRow : function(row)
10173     {
10174         //Roo.log('GridView.focusRow');
10175         var x = this.bodyEl.dom.scrollLeft;
10176         this.focusCell(row, 0, false);
10177         this.bodyEl.dom.scrollLeft = x;
10178
10179     },
10180      /**
10181      * Focuses the specified cell.
10182      * @param {Number} row The row index
10183      * @param {Number} col The column index
10184      * @param {Boolean} hscroll false to disable horizontal scrolling
10185      */
10186     focusCell : function(row, col, hscroll)
10187     {
10188         //Roo.log('GridView.focusCell');
10189         var el = this.ensureVisible(row, col, hscroll);
10190         // not sure what focusEL achives = it's a <a> pos relative 
10191         //this.focusEl.alignTo(el, "tl-tl");
10192         //if(Roo.isGecko){
10193         //    this.focusEl.focus();
10194         //}else{
10195         //    this.focusEl.focus.defer(1, this.focusEl);
10196         //}
10197     },
10198     
10199      /**
10200      * Scrolls the specified cell into view
10201      * @param {Number} row The row index
10202      * @param {Number} col The column index
10203      * @param {Boolean} hscroll false to disable horizontal scrolling
10204      */
10205     ensureVisible : function(row, col, hscroll)
10206     {
10207         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10208         //return null; //disable for testing.
10209         if(typeof row != "number"){
10210             row = row.rowIndex;
10211         }
10212         if(row < 0 && row >= this.ds.getCount()){
10213             return  null;
10214         }
10215         col = (col !== undefined ? col : 0);
10216         var cm = this.cm;
10217         while(cm.isHidden(col)){
10218             col++;
10219         }
10220
10221         var el = this.getCellDom(row, col);
10222         if(!el){
10223             return null;
10224         }
10225         var c = this.bodyEl.dom;
10226
10227         var ctop = parseInt(el.offsetTop, 10);
10228         var cleft = parseInt(el.offsetLeft, 10);
10229         var cbot = ctop + el.offsetHeight;
10230         var cright = cleft + el.offsetWidth;
10231
10232         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10233         var ch = 0; //?? header is not withing the area?
10234         var stop = parseInt(c.scrollTop, 10);
10235         var sleft = parseInt(c.scrollLeft, 10);
10236         var sbot = stop + ch;
10237         var sright = sleft + c.clientWidth;
10238         /*
10239         Roo.log('GridView.ensureVisible:' +
10240                 ' ctop:' + ctop +
10241                 ' c.clientHeight:' + c.clientHeight +
10242                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10243                 ' stop:' + stop +
10244                 ' cbot:' + cbot +
10245                 ' sbot:' + sbot +
10246                 ' ch:' + ch  
10247                 );
10248         */
10249         if(ctop < stop){
10250             c.scrollTop = ctop;
10251             //Roo.log("set scrolltop to ctop DISABLE?");
10252         }else if(cbot > sbot){
10253             //Roo.log("set scrolltop to cbot-ch");
10254             c.scrollTop = cbot-ch;
10255         }
10256
10257         if(hscroll !== false){
10258             if(cleft < sleft){
10259                 c.scrollLeft = cleft;
10260             }else if(cright > sright){
10261                 c.scrollLeft = cright-c.clientWidth;
10262             }
10263         }
10264
10265         return el;
10266     },
10267     
10268     
10269     insertRow : function(dm, rowIndex, isUpdate){
10270         
10271         if(!isUpdate){
10272             this.fireEvent("beforerowsinserted", this, rowIndex);
10273         }
10274             //var s = this.getScrollState();
10275         var row = this.renderRow(this.cm, this.store, rowIndex);
10276         // insert before rowIndex..
10277         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10278         
10279         var _this = this;
10280                 
10281         if(row.cellObjects.length){
10282             Roo.each(row.cellObjects, function(r){
10283                 _this.renderCellObject(r);
10284             })
10285         }
10286             
10287         if(!isUpdate){
10288             this.fireEvent("rowsinserted", this, rowIndex);
10289             //this.syncRowHeights(firstRow, lastRow);
10290             //this.stripeRows(firstRow);
10291             //this.layout();
10292         }
10293         
10294     },
10295     
10296     
10297     getRowDom : function(rowIndex)
10298     {
10299         var rows = this.el.select('tbody > tr', true).elements;
10300         
10301         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10302         
10303     },
10304     getCellDom : function(rowIndex, colIndex)
10305     {
10306         var row = this.getRowDom(rowIndex);
10307         if (row === false) {
10308             return false;
10309         }
10310         var cols = row.select('td', true).elements;
10311         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10312         
10313     },
10314     
10315     // returns the object tree for a tr..
10316   
10317     
10318     renderRow : function(cm, ds, rowIndex) 
10319     {
10320         var d = ds.getAt(rowIndex);
10321         
10322         var row = {
10323             tag : 'tr',
10324             cls : 'x-row-' + rowIndex,
10325             cn : []
10326         };
10327             
10328         var cellObjects = [];
10329         
10330         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10331             var config = cm.config[i];
10332             
10333             var renderer = cm.getRenderer(i);
10334             var value = '';
10335             var id = false;
10336             
10337             if(typeof(renderer) !== 'undefined'){
10338                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10339             }
10340             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10341             // and are rendered into the cells after the row is rendered - using the id for the element.
10342             
10343             if(typeof(value) === 'object'){
10344                 id = Roo.id();
10345                 cellObjects.push({
10346                     container : id,
10347                     cfg : value 
10348                 })
10349             }
10350             
10351             var rowcfg = {
10352                 record: d,
10353                 rowIndex : rowIndex,
10354                 colIndex : i,
10355                 rowClass : ''
10356             };
10357
10358             this.fireEvent('rowclass', this, rowcfg);
10359             
10360             var td = {
10361                 tag: 'td',
10362                 // this might end up displaying HTML?
10363                 // this is too messy... - better to only do it on columsn you know are going to be too long
10364                 //tooltip : (typeof(value) === 'object') ? '' : value,
10365                 cls : rowcfg.rowClass + ' x-col-' + i,
10366                 style: '',
10367                 html: (typeof(value) === 'object') ? '' : value
10368             };
10369             
10370             if (id) {
10371                 td.id = id;
10372             }
10373             
10374             if(typeof(config.colspan) != 'undefined'){
10375                 td.colspan = config.colspan;
10376             }
10377             
10378             
10379             
10380             if(typeof(config.align) != 'undefined' && config.align.length){
10381                 td.style += ' text-align:' + config.align + ';';
10382             }
10383             if(typeof(config.valign) != 'undefined' && config.valign.length){
10384                 td.style += ' vertical-align:' + config.valign + ';';
10385             }
10386             /*
10387             if(typeof(config.width) != 'undefined'){
10388                 td.style += ' width:' +  config.width + 'px;';
10389             }
10390             */
10391             
10392             if(typeof(config.cursor) != 'undefined'){
10393                 td.style += ' cursor:' +  config.cursor + ';';
10394             }
10395             
10396             if(typeof(config.cls) != 'undefined'){
10397                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10398             }
10399             if (this.responsive) {
10400                 ['xs','sm','md','lg'].map(function(size){
10401                     
10402                     if(typeof(config[size]) == 'undefined'){
10403                         return;
10404                     }
10405                     
10406                     
10407                       
10408                     if (!config[size]) { // 0 = hidden
10409                         // BS 4 '0' is treated as hide that column and below.
10410                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10411                         return;
10412                     }
10413                     
10414                     td.cls += ' col-' + size + '-' + config[size] + (
10415                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10416                     );
10417                      
10418     
10419                 });
10420             }
10421             row.cn.push(td);
10422            
10423         }
10424         
10425         row.cellObjects = cellObjects;
10426         
10427         return row;
10428           
10429     },
10430     
10431     
10432     
10433     onBeforeLoad : function()
10434     {
10435         this.el.unmask(); // if needed.
10436     },
10437      /**
10438      * Remove all rows
10439      */
10440     clear : function()
10441     {
10442         this.el.select('tbody', true).first().dom.innerHTML = '';
10443     },
10444     /**
10445      * Show or hide a row.
10446      * @param {Number} rowIndex to show or hide
10447      * @param {Boolean} state hide
10448      */
10449     setRowVisibility : function(rowIndex, state)
10450     {
10451         var bt = this.bodyEl.dom;
10452         
10453         var rows = this.el.select('tbody > tr', true).elements;
10454         
10455         if(typeof(rows[rowIndex]) == 'undefined'){
10456             return;
10457         }
10458         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10459         
10460     },
10461     
10462     
10463     getSelectionModel : function(){
10464         if(!this.selModel){
10465             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10466         }
10467         return this.selModel;
10468     },
10469     /*
10470      * Render the Roo.bootstrap object from renderder
10471      */
10472     renderCellObject : function(r)
10473     {
10474         var _this = this;
10475         
10476         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10477         
10478         var t = r.cfg.render(r.container);
10479         
10480         if(r.cfg.cn){
10481             Roo.each(r.cfg.cn, function(c){
10482                 var child = {
10483                     container: t.getChildContainer(),
10484                     cfg: c
10485                 };
10486                 _this.renderCellObject(child);
10487             })
10488         }
10489     },
10490     /**
10491      * get the Row Index from a dom element.
10492      * @param {Roo.Element} row The row to look for
10493      * @returns {Number} the row
10494      */
10495     getRowIndex : function(row)
10496     {
10497         var rowIndex = -1;
10498         
10499         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10500             if(el != row){
10501                 return;
10502             }
10503             
10504             rowIndex = index;
10505         });
10506         
10507         return rowIndex;
10508     },
10509     /**
10510      * get the header TH element for columnIndex
10511      * @param {Number} columnIndex
10512      * @returns {Roo.Element}
10513      */
10514     getHeaderIndex: function(colIndex)
10515     {
10516         var cols = this.headEl.select('th', true).elements;
10517         return cols[colIndex]; 
10518     },
10519     /**
10520      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10521      * @param {domElement} cell to look for
10522      * @returns {Number} the column
10523      */
10524     getCellIndex : function(cell)
10525     {
10526         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10527         if(id){
10528             return parseInt(id[1], 10);
10529         }
10530         return 0;
10531     },
10532      /**
10533      * Returns the grid's underlying element = used by panel.Grid
10534      * @return {Element} The element
10535      */
10536     getGridEl : function(){
10537         return this.el;
10538     },
10539      /**
10540      * Forces a resize - used by panel.Grid
10541      * @return {Element} The element
10542      */
10543     autoSize : function()
10544     {
10545         if(this.disableAutoSize) {
10546             return;
10547         }
10548         //var ctr = Roo.get(this.container.dom.parentElement);
10549         var ctr = Roo.get(this.el.dom);
10550         
10551         var thd = this.getGridEl().select('thead',true).first();
10552         var tbd = this.getGridEl().select('tbody', true).first();
10553         var tfd = this.getGridEl().select('tfoot', true).first();
10554         
10555         var cw = ctr.getWidth();
10556         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10557         
10558         if (tbd) {
10559             
10560             tbd.setWidth(ctr.getWidth());
10561             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10562             // this needs fixing for various usage - currently only hydra job advers I think..
10563             //tdb.setHeight(
10564             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10565             //); 
10566             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10567             cw -= barsize;
10568         }
10569         cw = Math.max(cw, this.totalWidth);
10570         this.getGridEl().select('tbody tr',true).setWidth(cw);
10571         this.initCSS();
10572         
10573         // resize 'expandable coloumn?
10574         
10575         return; // we doe not have a view in this design..
10576         
10577     },
10578     onBodyScroll: function()
10579     {
10580         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10581         if(this.headEl){
10582             this.headEl.setStyle({
10583                 'position' : 'relative',
10584                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10585             });
10586         }
10587         
10588         if(this.lazyLoad){
10589             
10590             var scrollHeight = this.bodyEl.dom.scrollHeight;
10591             
10592             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10593             
10594             var height = this.bodyEl.getHeight();
10595             
10596             if(scrollHeight - height == scrollTop) {
10597                 
10598                 var total = this.ds.getTotalCount();
10599                 
10600                 if(this.footer.cursor + this.footer.pageSize < total){
10601                     
10602                     this.footer.ds.load({
10603                         params : {
10604                             start : this.footer.cursor + this.footer.pageSize,
10605                             limit : this.footer.pageSize
10606                         },
10607                         add : true
10608                     });
10609                 }
10610             }
10611             
10612         }
10613     },
10614     onColumnSplitterMoved : function(i, diff)
10615     {
10616         this.userResized = true;
10617         
10618         var cm = this.colModel;
10619         
10620         var w = this.getHeaderIndex(i).getWidth() + diff;
10621         
10622         
10623         cm.setColumnWidth(i, w, true);
10624         this.initCSS();
10625         //var cid = cm.getColumnId(i); << not used in this version?
10626        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10627         
10628         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10629         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10630         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10631 */
10632         //this.updateSplitters();
10633         //this.layout(); << ??
10634         this.fireEvent("columnresize", i, w);
10635     },
10636     onHeaderChange : function()
10637     {
10638         var header = this.renderHeader();
10639         var table = this.el.select('table', true).first();
10640         
10641         this.headEl.remove();
10642         this.headEl = table.createChild(header, this.bodyEl, false);
10643         
10644         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10645             e.on('click', this.sort, this);
10646         }, this);
10647         
10648         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10649             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10650         }
10651         
10652     },
10653     
10654     onHiddenChange : function(colModel, colIndex, hidden)
10655     {
10656         /*
10657         this.cm.setHidden()
10658         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10659         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10660         
10661         this.CSS.updateRule(thSelector, "display", "");
10662         this.CSS.updateRule(tdSelector, "display", "");
10663         
10664         if(hidden){
10665             this.CSS.updateRule(thSelector, "display", "none");
10666             this.CSS.updateRule(tdSelector, "display", "none");
10667         }
10668         */
10669         // onload calls initCSS()
10670         this.onHeaderChange();
10671         this.onLoad();
10672     },
10673     
10674     setColumnWidth: function(col_index, width)
10675     {
10676         // width = "md-2 xs-2..."
10677         if(!this.colModel.config[col_index]) {
10678             return;
10679         }
10680         
10681         var w = width.split(" ");
10682         
10683         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10684         
10685         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10686         
10687         
10688         for(var j = 0; j < w.length; j++) {
10689             
10690             if(!w[j]) {
10691                 continue;
10692             }
10693             
10694             var size_cls = w[j].split("-");
10695             
10696             if(!Number.isInteger(size_cls[1] * 1)) {
10697                 continue;
10698             }
10699             
10700             if(!this.colModel.config[col_index][size_cls[0]]) {
10701                 continue;
10702             }
10703             
10704             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10705                 continue;
10706             }
10707             
10708             h_row[0].classList.replace(
10709                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10710                 "col-"+size_cls[0]+"-"+size_cls[1]
10711             );
10712             
10713             for(var i = 0; i < rows.length; i++) {
10714                 
10715                 var size_cls = w[j].split("-");
10716                 
10717                 if(!Number.isInteger(size_cls[1] * 1)) {
10718                     continue;
10719                 }
10720                 
10721                 if(!this.colModel.config[col_index][size_cls[0]]) {
10722                     continue;
10723                 }
10724                 
10725                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10726                     continue;
10727                 }
10728                 
10729                 rows[i].classList.replace(
10730                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10731                     "col-"+size_cls[0]+"-"+size_cls[1]
10732                 );
10733             }
10734             
10735             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10736         }
10737     }
10738 });
10739
10740 // currently only used to find the split on drag.. 
10741 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10742
10743 /**
10744  * @depricated
10745 */
10746 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10747 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10748 /*
10749  * - LGPL
10750  *
10751  * table cell
10752  * 
10753  */
10754
10755 /**
10756  * @class Roo.bootstrap.TableCell
10757  * @extends Roo.bootstrap.Component
10758  * @children Roo.bootstrap.Component
10759  * @parent Roo.bootstrap.TableRow
10760  * Bootstrap TableCell class
10761  * 
10762  * @cfg {String} html cell contain text
10763  * @cfg {String} cls cell class
10764  * @cfg {String} tag cell tag (td|th) default td
10765  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10766  * @cfg {String} align Aligns the content in a cell
10767  * @cfg {String} axis Categorizes cells
10768  * @cfg {String} bgcolor Specifies the background color of a cell
10769  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10770  * @cfg {Number} colspan Specifies the number of columns a cell should span
10771  * @cfg {String} headers Specifies one or more header cells a cell is related to
10772  * @cfg {Number} height Sets the height of a cell
10773  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10774  * @cfg {Number} rowspan Sets the number of rows a cell should span
10775  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10776  * @cfg {String} valign Vertical aligns the content in a cell
10777  * @cfg {Number} width Specifies the width of a cell
10778  * 
10779  * @constructor
10780  * Create a new TableCell
10781  * @param {Object} config The config object
10782  */
10783
10784 Roo.bootstrap.TableCell = function(config){
10785     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10786 };
10787
10788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10789     
10790     html: false,
10791     cls: false,
10792     tag: false,
10793     abbr: false,
10794     align: false,
10795     axis: false,
10796     bgcolor: false,
10797     charoff: false,
10798     colspan: false,
10799     headers: false,
10800     height: false,
10801     nowrap: false,
10802     rowspan: false,
10803     scope: false,
10804     valign: false,
10805     width: false,
10806     
10807     
10808     getAutoCreate : function(){
10809         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10810         
10811         cfg = {
10812             tag: 'td'
10813         };
10814         
10815         if(this.tag){
10816             cfg.tag = this.tag;
10817         }
10818         
10819         if (this.html) {
10820             cfg.html=this.html
10821         }
10822         if (this.cls) {
10823             cfg.cls=this.cls
10824         }
10825         if (this.abbr) {
10826             cfg.abbr=this.abbr
10827         }
10828         if (this.align) {
10829             cfg.align=this.align
10830         }
10831         if (this.axis) {
10832             cfg.axis=this.axis
10833         }
10834         if (this.bgcolor) {
10835             cfg.bgcolor=this.bgcolor
10836         }
10837         if (this.charoff) {
10838             cfg.charoff=this.charoff
10839         }
10840         if (this.colspan) {
10841             cfg.colspan=this.colspan
10842         }
10843         if (this.headers) {
10844             cfg.headers=this.headers
10845         }
10846         if (this.height) {
10847             cfg.height=this.height
10848         }
10849         if (this.nowrap) {
10850             cfg.nowrap=this.nowrap
10851         }
10852         if (this.rowspan) {
10853             cfg.rowspan=this.rowspan
10854         }
10855         if (this.scope) {
10856             cfg.scope=this.scope
10857         }
10858         if (this.valign) {
10859             cfg.valign=this.valign
10860         }
10861         if (this.width) {
10862             cfg.width=this.width
10863         }
10864         
10865         
10866         return cfg;
10867     }
10868    
10869 });
10870
10871  
10872
10873  /*
10874  * - LGPL
10875  *
10876  * table row
10877  * 
10878  */
10879
10880 /**
10881  * @class Roo.bootstrap.TableRow
10882  * @extends Roo.bootstrap.Component
10883  * @children Roo.bootstrap.TableCell
10884  * @parent Roo.bootstrap.TableBody
10885  * Bootstrap TableRow class
10886  * @cfg {String} cls row class
10887  * @cfg {String} align Aligns the content in a table row
10888  * @cfg {String} bgcolor Specifies a background color for a table row
10889  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10890  * @cfg {String} valign Vertical aligns the content in a table row
10891  * 
10892  * @constructor
10893  * Create a new TableRow
10894  * @param {Object} config The config object
10895  */
10896
10897 Roo.bootstrap.TableRow = function(config){
10898     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10899 };
10900
10901 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10902     
10903     cls: false,
10904     align: false,
10905     bgcolor: false,
10906     charoff: false,
10907     valign: false,
10908     
10909     getAutoCreate : function(){
10910         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10911         
10912         cfg = {
10913             tag: 'tr'
10914         };
10915             
10916         if(this.cls){
10917             cfg.cls = this.cls;
10918         }
10919         if(this.align){
10920             cfg.align = this.align;
10921         }
10922         if(this.bgcolor){
10923             cfg.bgcolor = this.bgcolor;
10924         }
10925         if(this.charoff){
10926             cfg.charoff = this.charoff;
10927         }
10928         if(this.valign){
10929             cfg.valign = this.valign;
10930         }
10931         
10932         return cfg;
10933     }
10934    
10935 });
10936
10937  
10938
10939  /*
10940  * - LGPL
10941  *
10942  * table body
10943  * 
10944  */
10945
10946 /**
10947  * @class Roo.bootstrap.TableBody
10948  * @extends Roo.bootstrap.Component
10949  * @children Roo.bootstrap.TableRow
10950  * @parent Roo.bootstrap.Table
10951  * Bootstrap TableBody class
10952  * @cfg {String} cls element class
10953  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10954  * @cfg {String} align Aligns the content inside the element
10955  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10956  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10957  * 
10958  * @constructor
10959  * Create a new TableBody
10960  * @param {Object} config The config object
10961  */
10962
10963 Roo.bootstrap.TableBody = function(config){
10964     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10965 };
10966
10967 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10968     
10969     cls: false,
10970     tag: false,
10971     align: false,
10972     charoff: false,
10973     valign: false,
10974     
10975     getAutoCreate : function(){
10976         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10977         
10978         cfg = {
10979             tag: 'tbody'
10980         };
10981             
10982         if (this.cls) {
10983             cfg.cls=this.cls
10984         }
10985         if(this.tag){
10986             cfg.tag = this.tag;
10987         }
10988         
10989         if(this.align){
10990             cfg.align = this.align;
10991         }
10992         if(this.charoff){
10993             cfg.charoff = this.charoff;
10994         }
10995         if(this.valign){
10996             cfg.valign = this.valign;
10997         }
10998         
10999         return cfg;
11000     }
11001     
11002     
11003 //    initEvents : function()
11004 //    {
11005 //        
11006 //        if(!this.store){
11007 //            return;
11008 //        }
11009 //        
11010 //        this.store = Roo.factory(this.store, Roo.data);
11011 //        this.store.on('load', this.onLoad, this);
11012 //        
11013 //        this.store.load();
11014 //        
11015 //    },
11016 //    
11017 //    onLoad: function () 
11018 //    {   
11019 //        this.fireEvent('load', this);
11020 //    }
11021 //    
11022 //   
11023 });
11024
11025  
11026
11027  /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038 // as we use this in bootstrap.
11039 Roo.namespace('Roo.form');
11040  /**
11041  * @class Roo.form.Action
11042  * Internal Class used to handle form actions
11043  * @constructor
11044  * @param {Roo.form.BasicForm} el The form element or its id
11045  * @param {Object} config Configuration options
11046  */
11047
11048  
11049  
11050 // define the action interface
11051 Roo.form.Action = function(form, options){
11052     this.form = form;
11053     this.options = options || {};
11054 };
11055 /**
11056  * Client Validation Failed
11057  * @const 
11058  */
11059 Roo.form.Action.CLIENT_INVALID = 'client';
11060 /**
11061  * Server Validation Failed
11062  * @const 
11063  */
11064 Roo.form.Action.SERVER_INVALID = 'server';
11065  /**
11066  * Connect to Server Failed
11067  * @const 
11068  */
11069 Roo.form.Action.CONNECT_FAILURE = 'connect';
11070 /**
11071  * Reading Data from Server Failed
11072  * @const 
11073  */
11074 Roo.form.Action.LOAD_FAILURE = 'load';
11075
11076 Roo.form.Action.prototype = {
11077     type : 'default',
11078     failureType : undefined,
11079     response : undefined,
11080     result : undefined,
11081
11082     // interface method
11083     run : function(options){
11084
11085     },
11086
11087     // interface method
11088     success : function(response){
11089
11090     },
11091
11092     // interface method
11093     handleResponse : function(response){
11094
11095     },
11096
11097     // default connection failure
11098     failure : function(response){
11099         
11100         this.response = response;
11101         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11102         this.form.afterAction(this, false);
11103     },
11104
11105     processResponse : function(response){
11106         this.response = response;
11107         if(!response.responseText){
11108             return true;
11109         }
11110         this.result = this.handleResponse(response);
11111         return this.result;
11112     },
11113
11114     // utility functions used internally
11115     getUrl : function(appendParams){
11116         var url = this.options.url || this.form.url || this.form.el.dom.action;
11117         if(appendParams){
11118             var p = this.getParams();
11119             if(p){
11120                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11121             }
11122         }
11123         return url;
11124     },
11125
11126     getMethod : function(){
11127         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11128     },
11129
11130     getParams : function(){
11131         var bp = this.form.baseParams;
11132         var p = this.options.params;
11133         if(p){
11134             if(typeof p == "object"){
11135                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11136             }else if(typeof p == 'string' && bp){
11137                 p += '&' + Roo.urlEncode(bp);
11138             }
11139         }else if(bp){
11140             p = Roo.urlEncode(bp);
11141         }
11142         return p;
11143     },
11144
11145     createCallback : function(){
11146         return {
11147             success: this.success,
11148             failure: this.failure,
11149             scope: this,
11150             timeout: (this.form.timeout*1000),
11151             upload: this.form.fileUpload ? this.success : undefined
11152         };
11153     }
11154 };
11155
11156 Roo.form.Action.Submit = function(form, options){
11157     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11158 };
11159
11160 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11161     type : 'submit',
11162
11163     haveProgress : false,
11164     uploadComplete : false,
11165     
11166     // uploadProgress indicator.
11167     uploadProgress : function()
11168     {
11169         if (!this.form.progressUrl) {
11170             return;
11171         }
11172         
11173         if (!this.haveProgress) {
11174             Roo.MessageBox.progress("Uploading", "Uploading");
11175         }
11176         if (this.uploadComplete) {
11177            Roo.MessageBox.hide();
11178            return;
11179         }
11180         
11181         this.haveProgress = true;
11182    
11183         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11184         
11185         var c = new Roo.data.Connection();
11186         c.request({
11187             url : this.form.progressUrl,
11188             params: {
11189                 id : uid
11190             },
11191             method: 'GET',
11192             success : function(req){
11193                //console.log(data);
11194                 var rdata = false;
11195                 var edata;
11196                 try  {
11197                    rdata = Roo.decode(req.responseText)
11198                 } catch (e) {
11199                     Roo.log("Invalid data from server..");
11200                     Roo.log(edata);
11201                     return;
11202                 }
11203                 if (!rdata || !rdata.success) {
11204                     Roo.log(rdata);
11205                     Roo.MessageBox.alert(Roo.encode(rdata));
11206                     return;
11207                 }
11208                 var data = rdata.data;
11209                 
11210                 if (this.uploadComplete) {
11211                    Roo.MessageBox.hide();
11212                    return;
11213                 }
11214                    
11215                 if (data){
11216                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11217                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11218                     );
11219                 }
11220                 this.uploadProgress.defer(2000,this);
11221             },
11222        
11223             failure: function(data) {
11224                 Roo.log('progress url failed ');
11225                 Roo.log(data);
11226             },
11227             scope : this
11228         });
11229            
11230     },
11231     
11232     
11233     run : function()
11234     {
11235         // run get Values on the form, so it syncs any secondary forms.
11236         this.form.getValues();
11237         
11238         var o = this.options;
11239         var method = this.getMethod();
11240         var isPost = method == 'POST';
11241         if(o.clientValidation === false || this.form.isValid()){
11242             
11243             if (this.form.progressUrl) {
11244                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11245                     (new Date() * 1) + '' + Math.random());
11246                     
11247             } 
11248             
11249             
11250             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11251                 form:this.form.el.dom,
11252                 url:this.getUrl(!isPost),
11253                 method: method,
11254                 params:isPost ? this.getParams() : null,
11255                 isUpload: this.form.fileUpload,
11256                 formData : this.form.formData
11257             }));
11258             
11259             this.uploadProgress();
11260
11261         }else if (o.clientValidation !== false){ // client validation failed
11262             this.failureType = Roo.form.Action.CLIENT_INVALID;
11263             this.form.afterAction(this, false);
11264         }
11265     },
11266
11267     success : function(response)
11268     {
11269         this.uploadComplete= true;
11270         if (this.haveProgress) {
11271             Roo.MessageBox.hide();
11272         }
11273         
11274         
11275         var result = this.processResponse(response);
11276         if(result === true || result.success){
11277             this.form.afterAction(this, true);
11278             return;
11279         }
11280         if(result.errors){
11281             this.form.markInvalid(result.errors);
11282             this.failureType = Roo.form.Action.SERVER_INVALID;
11283         }
11284         this.form.afterAction(this, false);
11285     },
11286     failure : function(response)
11287     {
11288         this.uploadComplete= true;
11289         if (this.haveProgress) {
11290             Roo.MessageBox.hide();
11291         }
11292         
11293         this.response = response;
11294         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11295         this.form.afterAction(this, false);
11296     },
11297     
11298     handleResponse : function(response){
11299         if(this.form.errorReader){
11300             var rs = this.form.errorReader.read(response);
11301             var errors = [];
11302             if(rs.records){
11303                 for(var i = 0, len = rs.records.length; i < len; i++) {
11304                     var r = rs.records[i];
11305                     errors[i] = r.data;
11306                 }
11307             }
11308             if(errors.length < 1){
11309                 errors = null;
11310             }
11311             return {
11312                 success : rs.success,
11313                 errors : errors
11314             };
11315         }
11316         var ret = false;
11317         try {
11318             var rt = response.responseText;
11319             if (rt.match(/^\<!--\[CDATA\[/)) {
11320                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11321                 rt = rt.replace(/\]\]--\>$/,'');
11322             }
11323             
11324             ret = Roo.decode(rt);
11325         } catch (e) {
11326             ret = {
11327                 success: false,
11328                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11329                 errors : []
11330             };
11331         }
11332         return ret;
11333         
11334     }
11335 });
11336
11337
11338 Roo.form.Action.Load = function(form, options){
11339     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11340     this.reader = this.form.reader;
11341 };
11342
11343 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11344     type : 'load',
11345
11346     run : function(){
11347         
11348         Roo.Ajax.request(Roo.apply(
11349                 this.createCallback(), {
11350                     method:this.getMethod(),
11351                     url:this.getUrl(false),
11352                     params:this.getParams()
11353         }));
11354     },
11355
11356     success : function(response){
11357         
11358         var result = this.processResponse(response);
11359         if(result === true || !result.success || !result.data){
11360             this.failureType = Roo.form.Action.LOAD_FAILURE;
11361             this.form.afterAction(this, false);
11362             return;
11363         }
11364         this.form.clearInvalid();
11365         this.form.setValues(result.data);
11366         this.form.afterAction(this, true);
11367     },
11368
11369     handleResponse : function(response){
11370         if(this.form.reader){
11371             var rs = this.form.reader.read(response);
11372             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11373             return {
11374                 success : rs.success,
11375                 data : data
11376             };
11377         }
11378         return Roo.decode(response.responseText);
11379     }
11380 });
11381
11382 Roo.form.Action.ACTION_TYPES = {
11383     'load' : Roo.form.Action.Load,
11384     'submit' : Roo.form.Action.Submit
11385 };/*
11386  * - LGPL
11387  *
11388  * form
11389  *
11390  */
11391
11392 /**
11393  * @class Roo.bootstrap.form.Form
11394  * @extends Roo.bootstrap.Component
11395  * @children Roo.bootstrap.Component
11396  * Bootstrap Form class
11397  * @cfg {String} method  GET | POST (default POST)
11398  * @cfg {String} labelAlign top | left (default top)
11399  * @cfg {String} align left  | right - for navbars
11400  * @cfg {Boolean} loadMask load mask when submit (default true)
11401
11402  *
11403  * @constructor
11404  * Create a new Form
11405  * @param {Object} config The config object
11406  */
11407
11408
11409 Roo.bootstrap.form.Form = function(config){
11410     
11411     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11412     
11413     Roo.bootstrap.form.Form.popover.apply();
11414     
11415     this.addEvents({
11416         /**
11417          * @event clientvalidation
11418          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11419          * @param {Form} this
11420          * @param {Boolean} valid true if the form has passed client-side validation
11421          */
11422         clientvalidation: true,
11423         /**
11424          * @event beforeaction
11425          * Fires before any action is performed. Return false to cancel the action.
11426          * @param {Form} this
11427          * @param {Action} action The action to be performed
11428          */
11429         beforeaction: true,
11430         /**
11431          * @event actionfailed
11432          * Fires when an action fails.
11433          * @param {Form} this
11434          * @param {Action} action The action that failed
11435          */
11436         actionfailed : true,
11437         /**
11438          * @event actioncomplete
11439          * Fires when an action is completed.
11440          * @param {Form} this
11441          * @param {Action} action The action that completed
11442          */
11443         actioncomplete : true
11444     });
11445 };
11446
11447 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11448
11449      /**
11450      * @cfg {String} method
11451      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11452      */
11453     method : 'POST',
11454     /**
11455      * @cfg {String} url
11456      * The URL to use for form actions if one isn't supplied in the action options.
11457      */
11458     /**
11459      * @cfg {Boolean} fileUpload
11460      * Set to true if this form is a file upload.
11461      */
11462
11463     /**
11464      * @cfg {Object} baseParams
11465      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11466      */
11467
11468     /**
11469      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11470      */
11471     timeout: 30,
11472     /**
11473      * @cfg {Sting} align (left|right) for navbar forms
11474      */
11475     align : 'left',
11476
11477     // private
11478     activeAction : null,
11479
11480     /**
11481      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11482      * element by passing it or its id or mask the form itself by passing in true.
11483      * @type Mixed
11484      */
11485     waitMsgTarget : false,
11486
11487     loadMask : true,
11488     
11489     /**
11490      * @cfg {Boolean} errorMask (true|false) default false
11491      */
11492     errorMask : false,
11493     
11494     /**
11495      * @cfg {Number} maskOffset Default 100
11496      */
11497     maskOffset : 100,
11498     
11499     /**
11500      * @cfg {Boolean} maskBody
11501      */
11502     maskBody : false,
11503
11504     getAutoCreate : function(){
11505
11506         var cfg = {
11507             tag: 'form',
11508             method : this.method || 'POST',
11509             id : this.id || Roo.id(),
11510             cls : ''
11511         };
11512         if (this.parent().xtype.match(/^Nav/)) {
11513             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11514
11515         }
11516
11517         if (this.labelAlign == 'left' ) {
11518             cfg.cls += ' form-horizontal';
11519         }
11520
11521
11522         return cfg;
11523     },
11524     initEvents : function()
11525     {
11526         this.el.on('submit', this.onSubmit, this);
11527         // this was added as random key presses on the form where triggering form submit.
11528         this.el.on('keypress', function(e) {
11529             if (e.getCharCode() != 13) {
11530                 return true;
11531             }
11532             // we might need to allow it for textareas.. and some other items.
11533             // check e.getTarget().
11534
11535             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11536                 return true;
11537             }
11538
11539             Roo.log("keypress blocked");
11540
11541             e.preventDefault();
11542             return false;
11543         });
11544         
11545     },
11546     // private
11547     onSubmit : function(e){
11548         e.stopEvent();
11549     },
11550
11551      /**
11552      * Returns true if client-side validation on the form is successful.
11553      * @return Boolean
11554      */
11555     isValid : function(){
11556         var items = this.getItems();
11557         var valid = true;
11558         var target = false;
11559         
11560         items.each(function(f){
11561             
11562             if(f.validate()){
11563                 return;
11564             }
11565             
11566             Roo.log('invalid field: ' + f.name);
11567             
11568             valid = false;
11569
11570             if(!target && f.el.isVisible(true)){
11571                 target = f;
11572             }
11573            
11574         });
11575         
11576         if(this.errorMask && !valid){
11577             Roo.bootstrap.form.Form.popover.mask(this, target);
11578         }
11579         
11580         return valid;
11581     },
11582     
11583     /**
11584      * Returns true if any fields in this form have changed since their original load.
11585      * @return Boolean
11586      */
11587     isDirty : function(){
11588         var dirty = false;
11589         var items = this.getItems();
11590         items.each(function(f){
11591            if(f.isDirty()){
11592                dirty = true;
11593                return false;
11594            }
11595            return true;
11596         });
11597         return dirty;
11598     },
11599      /**
11600      * Performs a predefined action (submit or load) or custom actions you define on this form.
11601      * @param {String} actionName The name of the action type
11602      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11603      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11604      * accept other config options):
11605      * <pre>
11606 Property          Type             Description
11607 ----------------  ---------------  ----------------------------------------------------------------------------------
11608 url               String           The url for the action (defaults to the form's url)
11609 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11610 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11611 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11612                                    validate the form on the client (defaults to false)
11613      * </pre>
11614      * @return {BasicForm} this
11615      */
11616     doAction : function(action, options){
11617         if(typeof action == 'string'){
11618             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11619         }
11620         if(this.fireEvent('beforeaction', this, action) !== false){
11621             this.beforeAction(action);
11622             action.run.defer(100, action);
11623         }
11624         return this;
11625     },
11626
11627     // private
11628     beforeAction : function(action){
11629         var o = action.options;
11630         
11631         if(this.loadMask){
11632             
11633             if(this.maskBody){
11634                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11635             } else {
11636                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11637             }
11638         }
11639         // not really supported yet.. ??
11640
11641         //if(this.waitMsgTarget === true){
11642         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11643         //}else if(this.waitMsgTarget){
11644         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11645         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646         //}else {
11647         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11648        // }
11649
11650     },
11651
11652     // private
11653     afterAction : function(action, success){
11654         this.activeAction = null;
11655         var o = action.options;
11656
11657         if(this.loadMask){
11658             
11659             if(this.maskBody){
11660                 Roo.get(document.body).unmask();
11661             } else {
11662                 this.el.unmask();
11663             }
11664         }
11665         
11666         //if(this.waitMsgTarget === true){
11667 //            this.el.unmask();
11668         //}else if(this.waitMsgTarget){
11669         //    this.waitMsgTarget.unmask();
11670         //}else{
11671         //    Roo.MessageBox.updateProgress(1);
11672         //    Roo.MessageBox.hide();
11673        // }
11674         //
11675         if(success){
11676             if(o.reset){
11677                 this.reset();
11678             }
11679             Roo.callback(o.success, o.scope, [this, action]);
11680             this.fireEvent('actioncomplete', this, action);
11681
11682         }else{
11683
11684             // failure condition..
11685             // we have a scenario where updates need confirming.
11686             // eg. if a locking scenario exists..
11687             // we look for { errors : { needs_confirm : true }} in the response.
11688             if (
11689                 (typeof(action.result) != 'undefined')  &&
11690                 (typeof(action.result.errors) != 'undefined')  &&
11691                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11692            ){
11693                 var _t = this;
11694                 Roo.log("not supported yet");
11695                  /*
11696
11697                 Roo.MessageBox.confirm(
11698                     "Change requires confirmation",
11699                     action.result.errorMsg,
11700                     function(r) {
11701                         if (r != 'yes') {
11702                             return;
11703                         }
11704                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11705                     }
11706
11707                 );
11708                 */
11709
11710
11711                 return;
11712             }
11713
11714             Roo.callback(o.failure, o.scope, [this, action]);
11715             // show an error message if no failed handler is set..
11716             if (!this.hasListener('actionfailed')) {
11717                 Roo.log("need to add dialog support");
11718                 /*
11719                 Roo.MessageBox.alert("Error",
11720                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11721                         action.result.errorMsg :
11722                         "Saving Failed, please check your entries or try again"
11723                 );
11724                 */
11725             }
11726
11727             this.fireEvent('actionfailed', this, action);
11728         }
11729
11730     },
11731     /**
11732      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11733      * @param {String} id The value to search for
11734      * @return Field
11735      */
11736     findField : function(id){
11737         var items = this.getItems();
11738         var field = items.get(id);
11739         if(!field){
11740              items.each(function(f){
11741                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11742                     field = f;
11743                     return false;
11744                 }
11745                 return true;
11746             });
11747         }
11748         return field || null;
11749     },
11750      /**
11751      * Mark fields in this form invalid in bulk.
11752      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11753      * @return {BasicForm} this
11754      */
11755     markInvalid : function(errors){
11756         if(errors instanceof Array){
11757             for(var i = 0, len = errors.length; i < len; i++){
11758                 var fieldError = errors[i];
11759                 var f = this.findField(fieldError.id);
11760                 if(f){
11761                     f.markInvalid(fieldError.msg);
11762                 }
11763             }
11764         }else{
11765             var field, id;
11766             for(id in errors){
11767                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11768                     field.markInvalid(errors[id]);
11769                 }
11770             }
11771         }
11772         //Roo.each(this.childForms || [], function (f) {
11773         //    f.markInvalid(errors);
11774         //});
11775
11776         return this;
11777     },
11778
11779     /**
11780      * Set values for fields in this form in bulk.
11781      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11782      * @return {BasicForm} this
11783      */
11784     setValues : function(values){
11785         if(values instanceof Array){ // array of objects
11786             for(var i = 0, len = values.length; i < len; i++){
11787                 var v = values[i];
11788                 var f = this.findField(v.id);
11789                 if(f){
11790                     f.setValue(v.value);
11791                     if(this.trackResetOnLoad){
11792                         f.originalValue = f.getValue();
11793                     }
11794                 }
11795             }
11796         }else{ // object hash
11797             var field, id;
11798             for(id in values){
11799                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11800
11801                     if (field.setFromData &&
11802                         field.valueField &&
11803                         field.displayField &&
11804                         // combos' with local stores can
11805                         // be queried via setValue()
11806                         // to set their value..
11807                         (field.store && !field.store.isLocal)
11808                         ) {
11809                         // it's a combo
11810                         var sd = { };
11811                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11812                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11813                         field.setFromData(sd);
11814
11815                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11816                         
11817                         field.setFromData(values);
11818                         
11819                     } else {
11820                         field.setValue(values[id]);
11821                     }
11822
11823
11824                     if(this.trackResetOnLoad){
11825                         field.originalValue = field.getValue();
11826                     }
11827                 }
11828             }
11829         }
11830
11831         //Roo.each(this.childForms || [], function (f) {
11832         //    f.setValues(values);
11833         //});
11834
11835         return this;
11836     },
11837
11838     /**
11839      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11840      * they are returned as an array.
11841      * @param {Boolean} asString
11842      * @return {Object}
11843      */
11844     getValues : function(asString){
11845         //if (this.childForms) {
11846             // copy values from the child forms
11847         //    Roo.each(this.childForms, function (f) {
11848         //        this.setValues(f.getValues());
11849         //    }, this);
11850         //}
11851
11852
11853
11854         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11855         if(asString === true){
11856             return fs;
11857         }
11858         return Roo.urlDecode(fs);
11859     },
11860
11861     /**
11862      * Returns the fields in this form as an object with key/value pairs.
11863      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11864      * @return {Object}
11865      */
11866     getFieldValues : function(with_hidden)
11867     {
11868         var items = this.getItems();
11869         var ret = {};
11870         items.each(function(f){
11871             
11872             if (!f.getName()) {
11873                 return;
11874             }
11875             
11876             var v = f.getValue();
11877             
11878             if (f.inputType =='radio') {
11879                 if (typeof(ret[f.getName()]) == 'undefined') {
11880                     ret[f.getName()] = ''; // empty..
11881                 }
11882
11883                 if (!f.el.dom.checked) {
11884                     return;
11885
11886                 }
11887                 v = f.el.dom.value;
11888
11889             }
11890             
11891             if(f.xtype == 'MoneyField'){
11892                 ret[f.currencyName] = f.getCurrency();
11893             }
11894
11895             // not sure if this supported any more..
11896             if ((typeof(v) == 'object') && f.getRawValue) {
11897                 v = f.getRawValue() ; // dates..
11898             }
11899             // combo boxes where name != hiddenName...
11900             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11901                 ret[f.name] = f.getRawValue();
11902             }
11903             ret[f.getName()] = v;
11904         });
11905
11906         return ret;
11907     },
11908
11909     /**
11910      * Clears all invalid messages in this form.
11911      * @return {BasicForm} this
11912      */
11913     clearInvalid : function(){
11914         var items = this.getItems();
11915
11916         items.each(function(f){
11917            f.clearInvalid();
11918         });
11919
11920         return this;
11921     },
11922
11923     /**
11924      * Resets this form.
11925      * @return {BasicForm} this
11926      */
11927     reset : function(){
11928         var items = this.getItems();
11929         items.each(function(f){
11930             f.reset();
11931         });
11932
11933         Roo.each(this.childForms || [], function (f) {
11934             f.reset();
11935         });
11936
11937
11938         return this;
11939     },
11940     
11941     getItems : function()
11942     {
11943         var r=new Roo.util.MixedCollection(false, function(o){
11944             return o.id || (o.id = Roo.id());
11945         });
11946         var iter = function(el) {
11947             if (el.inputEl) {
11948                 r.add(el);
11949             }
11950             if (!el.items) {
11951                 return;
11952             }
11953             Roo.each(el.items,function(e) {
11954                 iter(e);
11955             });
11956         };
11957
11958         iter(this);
11959         return r;
11960     },
11961     
11962     hideFields : function(items)
11963     {
11964         Roo.each(items, function(i){
11965             
11966             var f = this.findField(i);
11967             
11968             if(!f){
11969                 return;
11970             }
11971             
11972             f.hide();
11973             
11974         }, this);
11975     },
11976     
11977     showFields : function(items)
11978     {
11979         Roo.each(items, function(i){
11980             
11981             var f = this.findField(i);
11982             
11983             if(!f){
11984                 return;
11985             }
11986             
11987             f.show();
11988             
11989         }, this);
11990     }
11991
11992 });
11993
11994 Roo.apply(Roo.bootstrap.form.Form, {
11995     
11996     popover : {
11997         
11998         padding : 5,
11999         
12000         isApplied : false,
12001         
12002         isMasked : false,
12003         
12004         form : false,
12005         
12006         target : false,
12007         
12008         toolTip : false,
12009         
12010         intervalID : false,
12011         
12012         maskEl : false,
12013         
12014         apply : function()
12015         {
12016             if(this.isApplied){
12017                 return;
12018             }
12019             
12020             this.maskEl = {
12021                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12022                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12023                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12024                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12025             };
12026             
12027             this.maskEl.top.enableDisplayMode("block");
12028             this.maskEl.left.enableDisplayMode("block");
12029             this.maskEl.bottom.enableDisplayMode("block");
12030             this.maskEl.right.enableDisplayMode("block");
12031             
12032             this.toolTip = new Roo.bootstrap.Tooltip({
12033                 cls : 'roo-form-error-popover',
12034                 alignment : {
12035                     'left' : ['r-l', [-2,0], 'right'],
12036                     'right' : ['l-r', [2,0], 'left'],
12037                     'bottom' : ['tl-bl', [0,2], 'top'],
12038                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12039                 }
12040             });
12041             
12042             this.toolTip.render(Roo.get(document.body));
12043
12044             this.toolTip.el.enableDisplayMode("block");
12045             
12046             Roo.get(document.body).on('click', function(){
12047                 this.unmask();
12048             }, this);
12049             
12050             Roo.get(document.body).on('touchstart', function(){
12051                 this.unmask();
12052             }, this);
12053             
12054             this.isApplied = true
12055         },
12056         
12057         mask : function(form, target)
12058         {
12059             this.form = form;
12060             
12061             this.target = target;
12062             
12063             if(!this.form.errorMask || !target.el){
12064                 return;
12065             }
12066             
12067             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12068             
12069             Roo.log(scrollable);
12070             
12071             var ot = this.target.el.calcOffsetsTo(scrollable);
12072             
12073             var scrollTo = ot[1] - this.form.maskOffset;
12074             
12075             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12076             
12077             scrollable.scrollTo('top', scrollTo);
12078             
12079             var box = this.target.el.getBox();
12080             Roo.log(box);
12081             var zIndex = Roo.bootstrap.Modal.zIndex++;
12082
12083             
12084             this.maskEl.top.setStyle('position', 'absolute');
12085             this.maskEl.top.setStyle('z-index', zIndex);
12086             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12087             this.maskEl.top.setLeft(0);
12088             this.maskEl.top.setTop(0);
12089             this.maskEl.top.show();
12090             
12091             this.maskEl.left.setStyle('position', 'absolute');
12092             this.maskEl.left.setStyle('z-index', zIndex);
12093             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12094             this.maskEl.left.setLeft(0);
12095             this.maskEl.left.setTop(box.y - this.padding);
12096             this.maskEl.left.show();
12097
12098             this.maskEl.bottom.setStyle('position', 'absolute');
12099             this.maskEl.bottom.setStyle('z-index', zIndex);
12100             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12101             this.maskEl.bottom.setLeft(0);
12102             this.maskEl.bottom.setTop(box.bottom + this.padding);
12103             this.maskEl.bottom.show();
12104
12105             this.maskEl.right.setStyle('position', 'absolute');
12106             this.maskEl.right.setStyle('z-index', zIndex);
12107             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12108             this.maskEl.right.setLeft(box.right + this.padding);
12109             this.maskEl.right.setTop(box.y - this.padding);
12110             this.maskEl.right.show();
12111
12112             this.toolTip.bindEl = this.target.el;
12113
12114             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12115
12116             var tip = this.target.blankText;
12117
12118             if(this.target.getValue() !== '' ) {
12119                 
12120                 if (this.target.invalidText.length) {
12121                     tip = this.target.invalidText;
12122                 } else if (this.target.regexText.length){
12123                     tip = this.target.regexText;
12124                 }
12125             }
12126
12127             this.toolTip.show(tip);
12128
12129             this.intervalID = window.setInterval(function() {
12130                 Roo.bootstrap.form.Form.popover.unmask();
12131             }, 10000);
12132
12133             window.onwheel = function(){ return false;};
12134             
12135             (function(){ this.isMasked = true; }).defer(500, this);
12136             
12137         },
12138         
12139         unmask : function()
12140         {
12141             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12142                 return;
12143             }
12144             
12145             this.maskEl.top.setStyle('position', 'absolute');
12146             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12147             this.maskEl.top.hide();
12148
12149             this.maskEl.left.setStyle('position', 'absolute');
12150             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12151             this.maskEl.left.hide();
12152
12153             this.maskEl.bottom.setStyle('position', 'absolute');
12154             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12155             this.maskEl.bottom.hide();
12156
12157             this.maskEl.right.setStyle('position', 'absolute');
12158             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12159             this.maskEl.right.hide();
12160             
12161             this.toolTip.hide();
12162             
12163             this.toolTip.el.hide();
12164             
12165             window.onwheel = function(){ return true;};
12166             
12167             if(this.intervalID){
12168                 window.clearInterval(this.intervalID);
12169                 this.intervalID = false;
12170             }
12171             
12172             this.isMasked = false;
12173             
12174         }
12175         
12176     }
12177     
12178 });
12179
12180 /*
12181  * Based on:
12182  * Ext JS Library 1.1.1
12183  * Copyright(c) 2006-2007, Ext JS, LLC.
12184  *
12185  * Originally Released Under LGPL - original licence link has changed is not relivant.
12186  *
12187  * Fork - LGPL
12188  * <script type="text/javascript">
12189  */
12190 /**
12191  * @class Roo.form.VTypes
12192  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12193  * @static
12194  */
12195 Roo.form.VTypes = function(){
12196     // closure these in so they are only created once.
12197     var alpha = /^[a-zA-Z_]+$/;
12198     var alphanum = /^[a-zA-Z0-9_]+$/;
12199     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12200     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12201
12202     // All these messages and functions are configurable
12203     return {
12204         /**
12205          * The function used to validate email addresses
12206          * @param {String} value The email address
12207          */
12208         email : function(v){
12209             return email.test(v);
12210         },
12211         /**
12212          * The error text to display when the email validation function returns false
12213          * @type String
12214          */
12215         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12216         /**
12217          * The keystroke filter mask to be applied on email input
12218          * @type RegExp
12219          */
12220         emailMask : /[a-z0-9_\.\-@]/i,
12221
12222         /**
12223          * The function used to validate URLs
12224          * @param {String} value The URL
12225          */
12226         url : function(v){
12227             return url.test(v);
12228         },
12229         /**
12230          * The error text to display when the url validation function returns false
12231          * @type String
12232          */
12233         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12234         
12235         /**
12236          * The function used to validate alpha values
12237          * @param {String} value The value
12238          */
12239         alpha : function(v){
12240             return alpha.test(v);
12241         },
12242         /**
12243          * The error text to display when the alpha validation function returns false
12244          * @type String
12245          */
12246         alphaText : 'This field should only contain letters and _',
12247         /**
12248          * The keystroke filter mask to be applied on alpha input
12249          * @type RegExp
12250          */
12251         alphaMask : /[a-z_]/i,
12252
12253         /**
12254          * The function used to validate alphanumeric values
12255          * @param {String} value The value
12256          */
12257         alphanum : function(v){
12258             return alphanum.test(v);
12259         },
12260         /**
12261          * The error text to display when the alphanumeric validation function returns false
12262          * @type String
12263          */
12264         alphanumText : 'This field should only contain letters, numbers and _',
12265         /**
12266          * The keystroke filter mask to be applied on alphanumeric input
12267          * @type RegExp
12268          */
12269         alphanumMask : /[a-z0-9_]/i
12270     };
12271 }();/*
12272  * - LGPL
12273  *
12274  * Input
12275  * 
12276  */
12277
12278 /**
12279  * @class Roo.bootstrap.form.Input
12280  * @extends Roo.bootstrap.Component
12281  * Bootstrap Input class
12282  * @cfg {Boolean} disabled is it disabled
12283  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12284  * @cfg {String} name name of the input
12285  * @cfg {string} fieldLabel - the label associated
12286  * @cfg {string} placeholder - placeholder to put in text.
12287  * @cfg {string} before - input group add on before
12288  * @cfg {string} after - input group add on after
12289  * @cfg {string} size - (lg|sm) or leave empty..
12290  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12291  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12292  * @cfg {Number} md colspan out of 12 for computer-sized screens
12293  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12294  * @cfg {string} value default value of the input
12295  * @cfg {Number} labelWidth set the width of label 
12296  * @cfg {Number} labellg set the width of label (1-12)
12297  * @cfg {Number} labelmd set the width of label (1-12)
12298  * @cfg {Number} labelsm set the width of label (1-12)
12299  * @cfg {Number} labelxs set the width of label (1-12)
12300  * @cfg {String} labelAlign (top|left)
12301  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12302  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12303  * @cfg {String} indicatorpos (left|right) default left
12304  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12305  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12306  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12307  * @cfg {Roo.bootstrap.Button} before Button to show before
12308  * @cfg {Roo.bootstrap.Button} afterButton to show before
12309  * @cfg {String} align (left|center|right) Default left
12310  * @cfg {Boolean} forceFeedback (true|false) Default false
12311  * 
12312  * @constructor
12313  * Create a new Input
12314  * @param {Object} config The config object
12315  */
12316
12317 Roo.bootstrap.form.Input = function(config){
12318     
12319     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12320     
12321     this.addEvents({
12322         /**
12323          * @event focus
12324          * Fires when this field receives input focus.
12325          * @param {Roo.form.Field} this
12326          */
12327         focus : true,
12328         /**
12329          * @event blur
12330          * Fires when this field loses input focus.
12331          * @param {Roo.form.Field} this
12332          */
12333         blur : true,
12334         /**
12335          * @event specialkey
12336          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12337          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12338          * @param {Roo.form.Field} this
12339          * @param {Roo.EventObject} e The event object
12340          */
12341         specialkey : true,
12342         /**
12343          * @event change
12344          * Fires just before the field blurs if the field value has changed.
12345          * @param {Roo.form.Field} this
12346          * @param {Mixed} newValue The new value
12347          * @param {Mixed} oldValue The original value
12348          */
12349         change : true,
12350         /**
12351          * @event invalid
12352          * Fires after the field has been marked as invalid.
12353          * @param {Roo.form.Field} this
12354          * @param {String} msg The validation message
12355          */
12356         invalid : true,
12357         /**
12358          * @event valid
12359          * Fires after the field has been validated with no errors.
12360          * @param {Roo.form.Field} this
12361          */
12362         valid : true,
12363          /**
12364          * @event keyup
12365          * Fires after the key up
12366          * @param {Roo.form.Field} this
12367          * @param {Roo.EventObject}  e The event Object
12368          */
12369         keyup : true,
12370         /**
12371          * @event paste
12372          * Fires after the user pastes into input
12373          * @param {Roo.form.Field} this
12374          * @param {Roo.EventObject}  e The event Object
12375          */
12376         paste : true
12377     });
12378 };
12379
12380 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12381      /**
12382      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12383       automatic validation (defaults to "keyup").
12384      */
12385     validationEvent : "keyup",
12386      /**
12387      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12388      */
12389     validateOnBlur : true,
12390     /**
12391      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12392      */
12393     validationDelay : 250,
12394      /**
12395      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12396      */
12397     focusClass : "x-form-focus",  // not needed???
12398     
12399        
12400     /**
12401      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12402      */
12403     invalidClass : "has-warning",
12404     
12405     /**
12406      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12407      */
12408     validClass : "has-success",
12409     
12410     /**
12411      * @cfg {Boolean} hasFeedback (true|false) default true
12412      */
12413     hasFeedback : true,
12414     
12415     /**
12416      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12417      */
12418     invalidFeedbackClass : "glyphicon-warning-sign",
12419     
12420     /**
12421      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12422      */
12423     validFeedbackClass : "glyphicon-ok",
12424     
12425     /**
12426      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12427      */
12428     selectOnFocus : false,
12429     
12430      /**
12431      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12432      */
12433     maskRe : null,
12434        /**
12435      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12436      */
12437     vtype : null,
12438     
12439       /**
12440      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12441      */
12442     disableKeyFilter : false,
12443     
12444        /**
12445      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12446      */
12447     disabled : false,
12448      /**
12449      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12450      */
12451     allowBlank : true,
12452     /**
12453      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12454      */
12455     blankText : "Please complete this mandatory field",
12456     
12457      /**
12458      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12459      */
12460     minLength : 0,
12461     /**
12462      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12463      */
12464     maxLength : Number.MAX_VALUE,
12465     /**
12466      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12467      */
12468     minLengthText : "The minimum length for this field is {0}",
12469     /**
12470      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12471      */
12472     maxLengthText : "The maximum length for this field is {0}",
12473   
12474     
12475     /**
12476      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12477      * If available, this function will be called only after the basic validators all return true, and will be passed the
12478      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12479      */
12480     validator : null,
12481     /**
12482      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12483      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12484      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12485      */
12486     regex : null,
12487     /**
12488      * @cfg {String} regexText -- Depricated - use Invalid Text
12489      */
12490     regexText : "",
12491     
12492     /**
12493      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12494      */
12495     invalidText : "",
12496     
12497     
12498     
12499     autocomplete: false,
12500     
12501     
12502     fieldLabel : '',
12503     inputType : 'text',
12504     
12505     name : false,
12506     placeholder: false,
12507     before : false,
12508     after : false,
12509     size : false,
12510     hasFocus : false,
12511     preventMark: false,
12512     isFormField : true,
12513     value : '',
12514     labelWidth : 2,
12515     labelAlign : false,
12516     readOnly : false,
12517     align : false,
12518     formatedValue : false,
12519     forceFeedback : false,
12520     
12521     indicatorpos : 'left',
12522     
12523     labellg : 0,
12524     labelmd : 0,
12525     labelsm : 0,
12526     labelxs : 0,
12527     
12528     capture : '',
12529     accept : '',
12530     
12531     parentLabelAlign : function()
12532     {
12533         var parent = this;
12534         while (parent.parent()) {
12535             parent = parent.parent();
12536             if (typeof(parent.labelAlign) !='undefined') {
12537                 return parent.labelAlign;
12538             }
12539         }
12540         return 'left';
12541         
12542     },
12543     
12544     getAutoCreate : function()
12545     {
12546         
12547         var id = Roo.id();
12548         
12549         var cfg = {};
12550         
12551         if(this.inputType != 'hidden'){
12552             cfg.cls = 'form-group' //input-group
12553         }
12554         
12555         var input =  {
12556             tag: 'input',
12557             id : id,
12558             type : this.inputType,
12559             value : this.value,
12560             cls : 'form-control',
12561             placeholder : this.placeholder || '',
12562             autocomplete : this.autocomplete || 'new-password'
12563         };
12564         if (this.inputType == 'file') {
12565             input.style = 'overflow:hidden'; // why not in CSS?
12566         }
12567         
12568         if(this.capture.length){
12569             input.capture = this.capture;
12570         }
12571         
12572         if(this.accept.length){
12573             input.accept = this.accept + "/*";
12574         }
12575         
12576         if(this.align){
12577             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12578         }
12579         
12580         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12581             input.maxLength = this.maxLength;
12582         }
12583         
12584         if (this.disabled) {
12585             input.disabled=true;
12586         }
12587         
12588         if (this.readOnly) {
12589             input.readonly=true;
12590         }
12591         
12592         if (this.name) {
12593             input.name = this.name;
12594         }
12595         
12596         if (this.size) {
12597             input.cls += ' input-' + this.size;
12598         }
12599         
12600         var settings=this;
12601         ['xs','sm','md','lg'].map(function(size){
12602             if (settings[size]) {
12603                 cfg.cls += ' col-' + size + '-' + settings[size];
12604             }
12605         });
12606         
12607         var inputblock = input;
12608         
12609         var feedback = {
12610             tag: 'span',
12611             cls: 'glyphicon form-control-feedback'
12612         };
12613             
12614         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12615             
12616             inputblock = {
12617                 cls : 'has-feedback',
12618                 cn :  [
12619                     input,
12620                     feedback
12621                 ] 
12622             };  
12623         }
12624         
12625         if (this.before || this.after) {
12626             
12627             inputblock = {
12628                 cls : 'input-group',
12629                 cn :  [] 
12630             };
12631             
12632             if (this.before && typeof(this.before) == 'string') {
12633                 
12634                 inputblock.cn.push({
12635                     tag :'span',
12636                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12637                     html : this.before
12638                 });
12639             }
12640             if (this.before && typeof(this.before) == 'object') {
12641                 this.before = Roo.factory(this.before);
12642                 
12643                 inputblock.cn.push({
12644                     tag :'span',
12645                     cls : 'roo-input-before input-group-prepend   input-group-' +
12646                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12647                 });
12648             }
12649             
12650             inputblock.cn.push(input);
12651             
12652             if (this.after && typeof(this.after) == 'string') {
12653                 inputblock.cn.push({
12654                     tag :'span',
12655                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12656                     html : this.after
12657                 });
12658             }
12659             if (this.after && typeof(this.after) == 'object') {
12660                 this.after = Roo.factory(this.after);
12661                 
12662                 inputblock.cn.push({
12663                     tag :'span',
12664                     cls : 'roo-input-after input-group-append  input-group-' +
12665                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12666                 });
12667             }
12668             
12669             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12670                 inputblock.cls += ' has-feedback';
12671                 inputblock.cn.push(feedback);
12672             }
12673         };
12674         
12675         
12676         
12677         cfg = this.getAutoCreateLabel( cfg, inputblock );
12678         
12679        
12680          
12681         
12682         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12683            cfg.cls += ' navbar-form';
12684         }
12685         
12686         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12687             // on BS4 we do this only if not form 
12688             cfg.cls += ' navbar-form';
12689             cfg.tag = 'li';
12690         }
12691         
12692         return cfg;
12693         
12694     },
12695     /**
12696      * autocreate the label - also used by textara... ?? and others?
12697      */
12698     getAutoCreateLabel : function( cfg, inputblock )
12699     {
12700         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12701        
12702         var indicator = {
12703             tag : 'i',
12704             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12705             tooltip : 'This field is required'
12706         };
12707         if (this.allowBlank ) {
12708             indicator.style = this.allowBlank ? ' display:none' : '';
12709         }
12710         if (align ==='left' && this.fieldLabel.length) {
12711             
12712             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12713             
12714             cfg.cn = [
12715                 indicator,
12716                 {
12717                     tag: 'label',
12718                     'for' :  id,
12719                     cls : 'control-label col-form-label',
12720                     html : this.fieldLabel
12721
12722                 },
12723                 {
12724                     cls : "", 
12725                     cn: [
12726                         inputblock
12727                     ]
12728                 }
12729             ];
12730             
12731             var labelCfg = cfg.cn[1];
12732             var contentCfg = cfg.cn[2];
12733             
12734             if(this.indicatorpos == 'right'){
12735                 cfg.cn = [
12736                     {
12737                         tag: 'label',
12738                         'for' :  id,
12739                         cls : 'control-label col-form-label',
12740                         cn : [
12741                             {
12742                                 tag : 'span',
12743                                 html : this.fieldLabel
12744                             },
12745                             indicator
12746                         ]
12747                     },
12748                     {
12749                         cls : "",
12750                         cn: [
12751                             inputblock
12752                         ]
12753                     }
12754
12755                 ];
12756                 
12757                 labelCfg = cfg.cn[0];
12758                 contentCfg = cfg.cn[1];
12759             
12760             }
12761             
12762             if(this.labelWidth > 12){
12763                 labelCfg.style = "width: " + this.labelWidth + 'px';
12764             }
12765             
12766             if(this.labelWidth < 13 && this.labelmd == 0){
12767                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12768             }
12769             
12770             if(this.labellg > 0){
12771                 labelCfg.cls += ' col-lg-' + this.labellg;
12772                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12773             }
12774             
12775             if(this.labelmd > 0){
12776                 labelCfg.cls += ' col-md-' + this.labelmd;
12777                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12778             }
12779             
12780             if(this.labelsm > 0){
12781                 labelCfg.cls += ' col-sm-' + this.labelsm;
12782                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12783             }
12784             
12785             if(this.labelxs > 0){
12786                 labelCfg.cls += ' col-xs-' + this.labelxs;
12787                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12788             }
12789             
12790             
12791         } else if ( this.fieldLabel.length) {
12792                 
12793             
12794             
12795             cfg.cn = [
12796                 {
12797                     tag : 'i',
12798                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12799                     tooltip : 'This field is required',
12800                     style : this.allowBlank ? ' display:none' : '' 
12801                 },
12802                 {
12803                     tag: 'label',
12804                    //cls : 'input-group-addon',
12805                     html : this.fieldLabel
12806
12807                 },
12808
12809                inputblock
12810
12811            ];
12812            
12813            if(this.indicatorpos == 'right'){
12814        
12815                 cfg.cn = [
12816                     {
12817                         tag: 'label',
12818                        //cls : 'input-group-addon',
12819                         html : this.fieldLabel
12820
12821                     },
12822                     {
12823                         tag : 'i',
12824                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12825                         tooltip : 'This field is required',
12826                         style : this.allowBlank ? ' display:none' : '' 
12827                     },
12828
12829                    inputblock
12830
12831                ];
12832
12833             }
12834
12835         } else {
12836             
12837             cfg.cn = [
12838
12839                     inputblock
12840
12841             ];
12842                 
12843                 
12844         };
12845         return cfg;
12846     },
12847     
12848     
12849     /**
12850      * return the real input element.
12851      */
12852     inputEl: function ()
12853     {
12854         return this.el.select('input.form-control',true).first();
12855     },
12856     
12857     tooltipEl : function()
12858     {
12859         return this.inputEl();
12860     },
12861     
12862     indicatorEl : function()
12863     {
12864         if (Roo.bootstrap.version == 4) {
12865             return false; // not enabled in v4 yet.
12866         }
12867         
12868         var indicator = this.el.select('i.roo-required-indicator',true).first();
12869         
12870         if(!indicator){
12871             return false;
12872         }
12873         
12874         return indicator;
12875         
12876     },
12877     
12878     setDisabled : function(v)
12879     {
12880         var i  = this.inputEl().dom;
12881         if (!v) {
12882             i.removeAttribute('disabled');
12883             return;
12884             
12885         }
12886         i.setAttribute('disabled','true');
12887     },
12888     initEvents : function()
12889     {
12890           
12891         this.inputEl().on("keydown" , this.fireKey,  this);
12892         this.inputEl().on("focus", this.onFocus,  this);
12893         this.inputEl().on("blur", this.onBlur,  this);
12894         
12895         this.inputEl().relayEvent('keyup', this);
12896         this.inputEl().relayEvent('paste', this);
12897         
12898         this.indicator = this.indicatorEl();
12899         
12900         if(this.indicator){
12901             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12902         }
12903  
12904         // reference to original value for reset
12905         this.originalValue = this.getValue();
12906         //Roo.form.TextField.superclass.initEvents.call(this);
12907         if(this.validationEvent == 'keyup'){
12908             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12909             this.inputEl().on('keyup', this.filterValidation, this);
12910         }
12911         else if(this.validationEvent !== false){
12912             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12913         }
12914         
12915         if(this.selectOnFocus){
12916             this.on("focus", this.preFocus, this);
12917             
12918         }
12919         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12920             this.inputEl().on("keypress", this.filterKeys, this);
12921         } else {
12922             this.inputEl().relayEvent('keypress', this);
12923         }
12924        /* if(this.grow){
12925             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12926             this.el.on("click", this.autoSize,  this);
12927         }
12928         */
12929         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12930             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12931         }
12932         
12933         if (typeof(this.before) == 'object') {
12934             this.before.render(this.el.select('.roo-input-before',true).first());
12935         }
12936         if (typeof(this.after) == 'object') {
12937             this.after.render(this.el.select('.roo-input-after',true).first());
12938         }
12939         
12940         this.inputEl().on('change', this.onChange, this);
12941         
12942     },
12943     filterValidation : function(e){
12944         if(!e.isNavKeyPress()){
12945             this.validationTask.delay(this.validationDelay);
12946         }
12947     },
12948      /**
12949      * Validates the field value
12950      * @return {Boolean} True if the value is valid, else false
12951      */
12952     validate : function(){
12953         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12954         if(this.disabled || this.validateValue(this.getRawValue())){
12955             this.markValid();
12956             return true;
12957         }
12958         
12959         this.markInvalid();
12960         return false;
12961     },
12962     
12963     
12964     /**
12965      * Validates a value according to the field's validation rules and marks the field as invalid
12966      * if the validation fails
12967      * @param {Mixed} value The value to validate
12968      * @return {Boolean} True if the value is valid, else false
12969      */
12970     validateValue : function(value)
12971     {
12972         if(this.getVisibilityEl().hasClass('hidden')){
12973             return true;
12974         }
12975         
12976         if(value.length < 1)  { // if it's blank
12977             if(this.allowBlank){
12978                 return true;
12979             }
12980             return false;
12981         }
12982         
12983         if(value.length < this.minLength){
12984             return false;
12985         }
12986         if(value.length > this.maxLength){
12987             return false;
12988         }
12989         if(this.vtype){
12990             var vt = Roo.form.VTypes;
12991             if(!vt[this.vtype](value, this)){
12992                 return false;
12993             }
12994         }
12995         if(typeof this.validator == "function"){
12996             var msg = this.validator(value);
12997             if (typeof(msg) == 'string') {
12998                 this.invalidText = msg;
12999             }
13000             if(msg !== true){
13001                 return false;
13002             }
13003         }
13004         
13005         if(this.regex && !this.regex.test(value)){
13006             return false;
13007         }
13008         
13009         return true;
13010     },
13011     
13012      // private
13013     fireKey : function(e){
13014         //Roo.log('field ' + e.getKey());
13015         if(e.isNavKeyPress()){
13016             this.fireEvent("specialkey", this, e);
13017         }
13018     },
13019     focus : function (selectText){
13020         if(this.rendered){
13021             this.inputEl().focus();
13022             if(selectText === true){
13023                 this.inputEl().dom.select();
13024             }
13025         }
13026         return this;
13027     } ,
13028     
13029     onFocus : function(){
13030         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13031            // this.el.addClass(this.focusClass);
13032         }
13033         if(!this.hasFocus){
13034             this.hasFocus = true;
13035             this.startValue = this.getValue();
13036             this.fireEvent("focus", this);
13037         }
13038     },
13039     
13040     beforeBlur : Roo.emptyFn,
13041
13042     
13043     // private
13044     onBlur : function(){
13045         this.beforeBlur();
13046         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13047             //this.el.removeClass(this.focusClass);
13048         }
13049         this.hasFocus = false;
13050         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13051             this.validate();
13052         }
13053         var v = this.getValue();
13054         if(String(v) !== String(this.startValue)){
13055             this.fireEvent('change', this, v, this.startValue);
13056         }
13057         this.fireEvent("blur", this);
13058     },
13059     
13060     onChange : function(e)
13061     {
13062         var v = this.getValue();
13063         if(String(v) !== String(this.startValue)){
13064             this.fireEvent('change', this, v, this.startValue);
13065         }
13066         
13067     },
13068     
13069     /**
13070      * Resets the current field value to the originally loaded value and clears any validation messages
13071      */
13072     reset : function(){
13073         this.setValue(this.originalValue);
13074         this.validate();
13075     },
13076      /**
13077      * Returns the name of the field
13078      * @return {Mixed} name The name field
13079      */
13080     getName: function(){
13081         return this.name;
13082     },
13083      /**
13084      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13085      * @return {Mixed} value The field value
13086      */
13087     getValue : function(){
13088         
13089         var v = this.inputEl().getValue();
13090         
13091         return v;
13092     },
13093     /**
13094      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13095      * @return {Mixed} value The field value
13096      */
13097     getRawValue : function(){
13098         var v = this.inputEl().getValue();
13099         
13100         return v;
13101     },
13102     
13103     /**
13104      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13105      * @param {Mixed} value The value to set
13106      */
13107     setRawValue : function(v){
13108         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13109     },
13110     
13111     selectText : function(start, end){
13112         var v = this.getRawValue();
13113         if(v.length > 0){
13114             start = start === undefined ? 0 : start;
13115             end = end === undefined ? v.length : end;
13116             var d = this.inputEl().dom;
13117             if(d.setSelectionRange){
13118                 d.setSelectionRange(start, end);
13119             }else if(d.createTextRange){
13120                 var range = d.createTextRange();
13121                 range.moveStart("character", start);
13122                 range.moveEnd("character", v.length-end);
13123                 range.select();
13124             }
13125         }
13126     },
13127     
13128     /**
13129      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13130      * @param {Mixed} value The value to set
13131      */
13132     setValue : function(v){
13133         this.value = v;
13134         if(this.rendered){
13135             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13136             this.validate();
13137         }
13138     },
13139     
13140     /*
13141     processValue : function(value){
13142         if(this.stripCharsRe){
13143             var newValue = value.replace(this.stripCharsRe, '');
13144             if(newValue !== value){
13145                 this.setRawValue(newValue);
13146                 return newValue;
13147             }
13148         }
13149         return value;
13150     },
13151   */
13152     preFocus : function(){
13153         
13154         if(this.selectOnFocus){
13155             this.inputEl().dom.select();
13156         }
13157     },
13158     filterKeys : function(e){
13159         var k = e.getKey();
13160         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13161             return;
13162         }
13163         var c = e.getCharCode(), cc = String.fromCharCode(c);
13164         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13165             return;
13166         }
13167         if(!this.maskRe.test(cc)){
13168             e.stopEvent();
13169         }
13170     },
13171      /**
13172      * Clear any invalid styles/messages for this field
13173      */
13174     clearInvalid : function(){
13175         
13176         if(!this.el || this.preventMark){ // not rendered
13177             return;
13178         }
13179         
13180         
13181         this.el.removeClass([this.invalidClass, 'is-invalid']);
13182         
13183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13184             
13185             var feedback = this.el.select('.form-control-feedback', true).first();
13186             
13187             if(feedback){
13188                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13189             }
13190             
13191         }
13192         
13193         if(this.indicator){
13194             this.indicator.removeClass('visible');
13195             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13196         }
13197         
13198         this.fireEvent('valid', this);
13199     },
13200     
13201      /**
13202      * Mark this field as valid
13203      */
13204     markValid : function()
13205     {
13206         if(!this.el  || this.preventMark){ // not rendered...
13207             return;
13208         }
13209         
13210         this.el.removeClass([this.invalidClass, this.validClass]);
13211         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13212
13213         var feedback = this.el.select('.form-control-feedback', true).first();
13214             
13215         if(feedback){
13216             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13217         }
13218         
13219         if(this.indicator){
13220             this.indicator.removeClass('visible');
13221             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13222         }
13223         
13224         if(this.disabled){
13225             return;
13226         }
13227         
13228            
13229         if(this.allowBlank && !this.getRawValue().length){
13230             return;
13231         }
13232         if (Roo.bootstrap.version == 3) {
13233             this.el.addClass(this.validClass);
13234         } else {
13235             this.inputEl().addClass('is-valid');
13236         }
13237
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13245             }
13246             
13247         }
13248         
13249         this.fireEvent('valid', this);
13250     },
13251     
13252      /**
13253      * Mark this field as invalid
13254      * @param {String} msg The validation message
13255      */
13256     markInvalid : function(msg)
13257     {
13258         if(!this.el  || this.preventMark){ // not rendered
13259             return;
13260         }
13261         
13262         this.el.removeClass([this.invalidClass, this.validClass]);
13263         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13264         
13265         var feedback = this.el.select('.form-control-feedback', true).first();
13266             
13267         if(feedback){
13268             this.el.select('.form-control-feedback', true).first().removeClass(
13269                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13270         }
13271
13272         if(this.disabled){
13273             return;
13274         }
13275         
13276         if(this.allowBlank && !this.getRawValue().length){
13277             return;
13278         }
13279         
13280         if(this.indicator){
13281             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13282             this.indicator.addClass('visible');
13283         }
13284         if (Roo.bootstrap.version == 3) {
13285             this.el.addClass(this.invalidClass);
13286         } else {
13287             this.inputEl().addClass('is-invalid');
13288         }
13289         
13290         
13291         
13292         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13293             
13294             var feedback = this.el.select('.form-control-feedback', true).first();
13295             
13296             if(feedback){
13297                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13298                 
13299                 if(this.getValue().length || this.forceFeedback){
13300                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13301                 }
13302                 
13303             }
13304             
13305         }
13306         
13307         this.fireEvent('invalid', this, msg);
13308     },
13309     // private
13310     SafariOnKeyDown : function(event)
13311     {
13312         // this is a workaround for a password hang bug on chrome/ webkit.
13313         if (this.inputEl().dom.type != 'password') {
13314             return;
13315         }
13316         
13317         var isSelectAll = false;
13318         
13319         if(this.inputEl().dom.selectionEnd > 0){
13320             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13321         }
13322         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13323             event.preventDefault();
13324             this.setValue('');
13325             return;
13326         }
13327         
13328         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13329             
13330             event.preventDefault();
13331             // this is very hacky as keydown always get's upper case.
13332             //
13333             var cc = String.fromCharCode(event.getCharCode());
13334             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13335             
13336         }
13337     },
13338     adjustWidth : function(tag, w){
13339         tag = tag.toLowerCase();
13340         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13341             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13342                 if(tag == 'input'){
13343                     return w + 2;
13344                 }
13345                 if(tag == 'textarea'){
13346                     return w-2;
13347                 }
13348             }else if(Roo.isOpera){
13349                 if(tag == 'input'){
13350                     return w + 2;
13351                 }
13352                 if(tag == 'textarea'){
13353                     return w-2;
13354                 }
13355             }
13356         }
13357         return w;
13358     },
13359     
13360     setFieldLabel : function(v)
13361     {
13362         if(!this.rendered){
13363             return;
13364         }
13365         
13366         if(this.indicatorEl()){
13367             var ar = this.el.select('label > span',true);
13368             
13369             if (ar.elements.length) {
13370                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13371                 this.fieldLabel = v;
13372                 return;
13373             }
13374             
13375             var br = this.el.select('label',true);
13376             
13377             if(br.elements.length) {
13378                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13379                 this.fieldLabel = v;
13380                 return;
13381             }
13382             
13383             Roo.log('Cannot Found any of label > span || label in input');
13384             return;
13385         }
13386         
13387         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13388         this.fieldLabel = v;
13389         
13390         
13391     }
13392 });
13393
13394  
13395 /*
13396  * - LGPL
13397  *
13398  * Input
13399  * 
13400  */
13401
13402 /**
13403  * @class Roo.bootstrap.form.TextArea
13404  * @extends Roo.bootstrap.form.Input
13405  * Bootstrap TextArea class
13406  * @cfg {Number} cols Specifies the visible width of a text area
13407  * @cfg {Number} rows Specifies the visible number of lines in a text area
13408  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13409  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13410  * @cfg {string} html text
13411  * 
13412  * @constructor
13413  * Create a new TextArea
13414  * @param {Object} config The config object
13415  */
13416
13417 Roo.bootstrap.form.TextArea = function(config){
13418     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13419    
13420 };
13421
13422 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13423      
13424     cols : false,
13425     rows : 5,
13426     readOnly : false,
13427     warp : 'soft',
13428     resize : false,
13429     value: false,
13430     html: false,
13431     
13432     getAutoCreate : function(){
13433         
13434         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13435         
13436         var id = Roo.id();
13437         
13438         var cfg = {};
13439         
13440         if(this.inputType != 'hidden'){
13441             cfg.cls = 'form-group' //input-group
13442         }
13443         
13444         var input =  {
13445             tag: 'textarea',
13446             id : id,
13447             warp : this.warp,
13448             rows : this.rows,
13449             value : this.value || '',
13450             html: this.html || '',
13451             cls : 'form-control',
13452             placeholder : this.placeholder || '' 
13453             
13454         };
13455         
13456         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13457             input.maxLength = this.maxLength;
13458         }
13459         
13460         if(this.resize){
13461             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13462         }
13463         
13464         if(this.cols){
13465             input.cols = this.cols;
13466         }
13467         
13468         if (this.readOnly) {
13469             input.readonly = true;
13470         }
13471         
13472         if (this.name) {
13473             input.name = this.name;
13474         }
13475         
13476         if (this.size) {
13477             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13478         }
13479         
13480         var settings=this;
13481         ['xs','sm','md','lg'].map(function(size){
13482             if (settings[size]) {
13483                 cfg.cls += ' col-' + size + '-' + settings[size];
13484             }
13485         });
13486         
13487         var inputblock = input;
13488         
13489         if(this.hasFeedback && !this.allowBlank){
13490             
13491             var feedback = {
13492                 tag: 'span',
13493                 cls: 'glyphicon form-control-feedback'
13494             };
13495
13496             inputblock = {
13497                 cls : 'has-feedback',
13498                 cn :  [
13499                     input,
13500                     feedback
13501                 ] 
13502             };  
13503         }
13504         
13505         
13506         if (this.before || this.after) {
13507             
13508             inputblock = {
13509                 cls : 'input-group',
13510                 cn :  [] 
13511             };
13512             if (this.before) {
13513                 inputblock.cn.push({
13514                     tag :'span',
13515                     cls : 'input-group-addon',
13516                     html : this.before
13517                 });
13518             }
13519             
13520             inputblock.cn.push(input);
13521             
13522             if(this.hasFeedback && !this.allowBlank){
13523                 inputblock.cls += ' has-feedback';
13524                 inputblock.cn.push(feedback);
13525             }
13526             
13527             if (this.after) {
13528                 inputblock.cn.push({
13529                     tag :'span',
13530                     cls : 'input-group-addon',
13531                     html : this.after
13532                 });
13533             }
13534             
13535         }
13536         
13537         
13538         cfg = this.getAutoCreateLabel( cfg, inputblock );
13539
13540          
13541         
13542         if (this.disabled) {
13543             input.disabled=true;
13544         }
13545         
13546         return cfg;
13547         
13548     },
13549     /**
13550      * return the real textarea element.
13551      */
13552     inputEl: function ()
13553     {
13554         return this.el.select('textarea.form-control',true).first();
13555     },
13556     
13557     /**
13558      * Clear any invalid styles/messages for this field
13559      */
13560     clearInvalid : function()
13561     {
13562         
13563         if(!this.el || this.preventMark){ // not rendered
13564             return;
13565         }
13566         
13567         var label = this.el.select('label', true).first();
13568         var icon = this.el.select('i.fa-star', true).first();
13569         
13570         if(label && icon){
13571             icon.remove();
13572         }
13573         this.el.removeClass( this.validClass);
13574         this.inputEl().removeClass('is-invalid');
13575          
13576         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13577             
13578             var feedback = this.el.select('.form-control-feedback', true).first();
13579             
13580             if(feedback){
13581                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13582             }
13583             
13584         }
13585         
13586         this.fireEvent('valid', this);
13587     },
13588     
13589      /**
13590      * Mark this field as valid
13591      */
13592     markValid : function()
13593     {
13594         if(!this.el  || this.preventMark){ // not rendered
13595             return;
13596         }
13597         
13598         this.el.removeClass([this.invalidClass, this.validClass]);
13599         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13600         
13601         var feedback = this.el.select('.form-control-feedback', true).first();
13602             
13603         if(feedback){
13604             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13605         }
13606
13607         if(this.disabled || this.allowBlank){
13608             return;
13609         }
13610         
13611         var label = this.el.select('label', true).first();
13612         var icon = this.el.select('i.fa-star', true).first();
13613         
13614         if(label && icon){
13615             icon.remove();
13616         }
13617         if (Roo.bootstrap.version == 3) {
13618             this.el.addClass(this.validClass);
13619         } else {
13620             this.inputEl().addClass('is-valid');
13621         }
13622         
13623         
13624         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13625             
13626             var feedback = this.el.select('.form-control-feedback', true).first();
13627             
13628             if(feedback){
13629                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13630                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13631             }
13632             
13633         }
13634         
13635         this.fireEvent('valid', this);
13636     },
13637     
13638      /**
13639      * Mark this field as invalid
13640      * @param {String} msg The validation message
13641      */
13642     markInvalid : function(msg)
13643     {
13644         if(!this.el  || this.preventMark){ // not rendered
13645             return;
13646         }
13647         
13648         this.el.removeClass([this.invalidClass, this.validClass]);
13649         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13650         
13651         var feedback = this.el.select('.form-control-feedback', true).first();
13652             
13653         if(feedback){
13654             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13655         }
13656
13657         if(this.disabled || this.allowBlank){
13658             return;
13659         }
13660         
13661         var label = this.el.select('label', true).first();
13662         var icon = this.el.select('i.fa-star', true).first();
13663         
13664         if(!this.getValue().length && label && !icon){
13665             this.el.createChild({
13666                 tag : 'i',
13667                 cls : 'text-danger fa fa-lg fa-star',
13668                 tooltip : 'This field is required',
13669                 style : 'margin-right:5px;'
13670             }, label, true);
13671         }
13672         
13673         if (Roo.bootstrap.version == 3) {
13674             this.el.addClass(this.invalidClass);
13675         } else {
13676             this.inputEl().addClass('is-invalid');
13677         }
13678         
13679         // fixme ... this may be depricated need to test..
13680         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13681             
13682             var feedback = this.el.select('.form-control-feedback', true).first();
13683             
13684             if(feedback){
13685                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13686                 
13687                 if(this.getValue().length || this.forceFeedback){
13688                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13689                 }
13690                 
13691             }
13692             
13693         }
13694         
13695         this.fireEvent('invalid', this, msg);
13696     }
13697 });
13698
13699  
13700 /*
13701  * - LGPL
13702  *
13703  * trigger field - base class for combo..
13704  * 
13705  */
13706  
13707 /**
13708  * @class Roo.bootstrap.form.TriggerField
13709  * @extends Roo.bootstrap.form.Input
13710  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13711  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13712  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13713  * for which you can provide a custom implementation.  For example:
13714  * <pre><code>
13715 var trigger = new Roo.bootstrap.form.TriggerField();
13716 trigger.onTriggerClick = myTriggerFn;
13717 trigger.applyTo('my-field');
13718 </code></pre>
13719  *
13720  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13721  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13722  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13723  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13724  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13725
13726  * @constructor
13727  * Create a new TriggerField.
13728  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13729  * to the base TextField)
13730  */
13731 Roo.bootstrap.form.TriggerField = function(config){
13732     this.mimicing = false;
13733     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13734 };
13735
13736 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13737     /**
13738      * @cfg {String} triggerClass A CSS class to apply to the trigger
13739      */
13740      /**
13741      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13742      */
13743     hideTrigger:false,
13744
13745     /**
13746      * @cfg {Boolean} removable (true|false) special filter default false
13747      */
13748     removable : false,
13749     
13750     /** @cfg {Boolean} grow @hide */
13751     /** @cfg {Number} growMin @hide */
13752     /** @cfg {Number} growMax @hide */
13753
13754     /**
13755      * @hide 
13756      * @method
13757      */
13758     autoSize: Roo.emptyFn,
13759     // private
13760     monitorTab : true,
13761     // private
13762     deferHeight : true,
13763
13764     
13765     actionMode : 'wrap',
13766     
13767     caret : false,
13768     
13769     
13770     getAutoCreate : function(){
13771        
13772         var align = this.labelAlign || this.parentLabelAlign();
13773         
13774         var id = Roo.id();
13775         
13776         var cfg = {
13777             cls: 'form-group' //input-group
13778         };
13779         
13780         
13781         var input =  {
13782             tag: 'input',
13783             id : id,
13784             type : this.inputType,
13785             cls : 'form-control',
13786             autocomplete: 'new-password',
13787             placeholder : this.placeholder || '' 
13788             
13789         };
13790         if (this.name) {
13791             input.name = this.name;
13792         }
13793         if (this.size) {
13794             input.cls += ' input-' + this.size;
13795         }
13796         
13797         if (this.disabled) {
13798             input.disabled=true;
13799         }
13800         
13801         var inputblock = input;
13802         
13803         if(this.hasFeedback && !this.allowBlank){
13804             
13805             var feedback = {
13806                 tag: 'span',
13807                 cls: 'glyphicon form-control-feedback'
13808             };
13809             
13810             if(this.removable && !this.editable  ){
13811                 inputblock = {
13812                     cls : 'has-feedback',
13813                     cn :  [
13814                         inputblock,
13815                         {
13816                             tag: 'button',
13817                             html : 'x',
13818                             cls : 'roo-combo-removable-btn close'
13819                         },
13820                         feedback
13821                     ] 
13822                 };
13823             } else {
13824                 inputblock = {
13825                     cls : 'has-feedback',
13826                     cn :  [
13827                         inputblock,
13828                         feedback
13829                     ] 
13830                 };
13831             }
13832
13833         } else {
13834             if(this.removable && !this.editable ){
13835                 inputblock = {
13836                     cls : 'roo-removable',
13837                     cn :  [
13838                         inputblock,
13839                         {
13840                             tag: 'button',
13841                             html : 'x',
13842                             cls : 'roo-combo-removable-btn close'
13843                         }
13844                     ] 
13845                 };
13846             }
13847         }
13848         
13849         if (this.before || this.after) {
13850             
13851             inputblock = {
13852                 cls : 'input-group',
13853                 cn :  [] 
13854             };
13855             if (this.before) {
13856                 inputblock.cn.push({
13857                     tag :'span',
13858                     cls : 'input-group-addon input-group-prepend input-group-text',
13859                     html : this.before
13860                 });
13861             }
13862             
13863             inputblock.cn.push(input);
13864             
13865             if(this.hasFeedback && !this.allowBlank){
13866                 inputblock.cls += ' has-feedback';
13867                 inputblock.cn.push(feedback);
13868             }
13869             
13870             if (this.after) {
13871                 inputblock.cn.push({
13872                     tag :'span',
13873                     cls : 'input-group-addon input-group-append input-group-text',
13874                     html : this.after
13875                 });
13876             }
13877             
13878         };
13879         
13880       
13881         
13882         var ibwrap = inputblock;
13883         
13884         if(this.multiple){
13885             ibwrap = {
13886                 tag: 'ul',
13887                 cls: 'roo-select2-choices',
13888                 cn:[
13889                     {
13890                         tag: 'li',
13891                         cls: 'roo-select2-search-field',
13892                         cn: [
13893
13894                             inputblock
13895                         ]
13896                     }
13897                 ]
13898             };
13899                 
13900         }
13901         
13902         var combobox = {
13903             cls: 'roo-select2-container input-group',
13904             cn: [
13905                  {
13906                     tag: 'input',
13907                     type : 'hidden',
13908                     cls: 'form-hidden-field'
13909                 },
13910                 ibwrap
13911             ]
13912         };
13913         
13914         if(!this.multiple && this.showToggleBtn){
13915             
13916             var caret = {
13917                         tag: 'span',
13918                         cls: 'caret'
13919              };
13920             if (this.caret != false) {
13921                 caret = {
13922                      tag: 'i',
13923                      cls: 'fa fa-' + this.caret
13924                 };
13925                 
13926             }
13927             
13928             combobox.cn.push({
13929                 tag :'span',
13930                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13931                 cn : [
13932                     Roo.bootstrap.version == 3 ? caret : '',
13933                     {
13934                         tag: 'span',
13935                         cls: 'combobox-clear',
13936                         cn  : [
13937                             {
13938                                 tag : 'i',
13939                                 cls: 'icon-remove'
13940                             }
13941                         ]
13942                     }
13943                 ]
13944
13945             })
13946         }
13947         
13948         if(this.multiple){
13949             combobox.cls += ' roo-select2-container-multi';
13950         }
13951          var indicator = {
13952             tag : 'i',
13953             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13954             tooltip : 'This field is required'
13955         };
13956         if (Roo.bootstrap.version == 4) {
13957             indicator = {
13958                 tag : 'i',
13959                 style : 'display:none'
13960             };
13961         }
13962         
13963         
13964         if (align ==='left' && this.fieldLabel.length) {
13965             
13966             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13967
13968             cfg.cn = [
13969                 indicator,
13970                 {
13971                     tag: 'label',
13972                     'for' :  id,
13973                     cls : 'control-label',
13974                     html : this.fieldLabel
13975
13976                 },
13977                 {
13978                     cls : "", 
13979                     cn: [
13980                         combobox
13981                     ]
13982                 }
13983
13984             ];
13985             
13986             var labelCfg = cfg.cn[1];
13987             var contentCfg = cfg.cn[2];
13988             
13989             if(this.indicatorpos == 'right'){
13990                 cfg.cn = [
13991                     {
13992                         tag: 'label',
13993                         'for' :  id,
13994                         cls : 'control-label',
13995                         cn : [
13996                             {
13997                                 tag : 'span',
13998                                 html : this.fieldLabel
13999                             },
14000                             indicator
14001                         ]
14002                     },
14003                     {
14004                         cls : "", 
14005                         cn: [
14006                             combobox
14007                         ]
14008                     }
14009
14010                 ];
14011                 
14012                 labelCfg = cfg.cn[0];
14013                 contentCfg = cfg.cn[1];
14014             }
14015             
14016             if(this.labelWidth > 12){
14017                 labelCfg.style = "width: " + this.labelWidth + 'px';
14018             }
14019             
14020             if(this.labelWidth < 13 && this.labelmd == 0){
14021                 this.labelmd = this.labelWidth;
14022             }
14023             
14024             if(this.labellg > 0){
14025                 labelCfg.cls += ' col-lg-' + this.labellg;
14026                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14027             }
14028             
14029             if(this.labelmd > 0){
14030                 labelCfg.cls += ' col-md-' + this.labelmd;
14031                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14032             }
14033             
14034             if(this.labelsm > 0){
14035                 labelCfg.cls += ' col-sm-' + this.labelsm;
14036                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14037             }
14038             
14039             if(this.labelxs > 0){
14040                 labelCfg.cls += ' col-xs-' + this.labelxs;
14041                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14042             }
14043             
14044         } else if ( this.fieldLabel.length) {
14045 //                Roo.log(" label");
14046             cfg.cn = [
14047                 indicator,
14048                {
14049                    tag: 'label',
14050                    //cls : 'input-group-addon',
14051                    html : this.fieldLabel
14052
14053                },
14054
14055                combobox
14056
14057             ];
14058             
14059             if(this.indicatorpos == 'right'){
14060                 
14061                 cfg.cn = [
14062                     {
14063                        tag: 'label',
14064                        cn : [
14065                            {
14066                                tag : 'span',
14067                                html : this.fieldLabel
14068                            },
14069                            indicator
14070                        ]
14071
14072                     },
14073                     combobox
14074
14075                 ];
14076
14077             }
14078
14079         } else {
14080             
14081 //                Roo.log(" no label && no align");
14082                 cfg = combobox
14083                      
14084                 
14085         }
14086         
14087         var settings=this;
14088         ['xs','sm','md','lg'].map(function(size){
14089             if (settings[size]) {
14090                 cfg.cls += ' col-' + size + '-' + settings[size];
14091             }
14092         });
14093         
14094         return cfg;
14095         
14096     },
14097     
14098     
14099     
14100     // private
14101     onResize : function(w, h){
14102 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14103 //        if(typeof w == 'number'){
14104 //            var x = w - this.trigger.getWidth();
14105 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14106 //            this.trigger.setStyle('left', x+'px');
14107 //        }
14108     },
14109
14110     // private
14111     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14112
14113     // private
14114     getResizeEl : function(){
14115         return this.inputEl();
14116     },
14117
14118     // private
14119     getPositionEl : function(){
14120         return this.inputEl();
14121     },
14122
14123     // private
14124     alignErrorIcon : function(){
14125         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14126     },
14127
14128     // private
14129     initEvents : function(){
14130         
14131         this.createList();
14132         
14133         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14134         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14135         if(!this.multiple && this.showToggleBtn){
14136             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14137             if(this.hideTrigger){
14138                 this.trigger.setDisplayed(false);
14139             }
14140             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14141         }
14142         
14143         if(this.multiple){
14144             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14145         }
14146         
14147         if(this.removable && !this.editable && !this.tickable){
14148             var close = this.closeTriggerEl();
14149             
14150             if(close){
14151                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14152                 close.on('click', this.removeBtnClick, this, close);
14153             }
14154         }
14155         
14156         //this.trigger.addClassOnOver('x-form-trigger-over');
14157         //this.trigger.addClassOnClick('x-form-trigger-click');
14158         
14159         //if(!this.width){
14160         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14161         //}
14162     },
14163     
14164     closeTriggerEl : function()
14165     {
14166         var close = this.el.select('.roo-combo-removable-btn', true).first();
14167         return close ? close : false;
14168     },
14169     
14170     removeBtnClick : function(e, h, el)
14171     {
14172         e.preventDefault();
14173         
14174         if(this.fireEvent("remove", this) !== false){
14175             this.reset();
14176             this.fireEvent("afterremove", this)
14177         }
14178     },
14179     
14180     createList : function()
14181     {
14182         this.list = Roo.get(document.body).createChild({
14183             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14184             cls: 'typeahead typeahead-long dropdown-menu shadow',
14185             style: 'display:none'
14186         });
14187         
14188         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14189         
14190     },
14191
14192     // private
14193     initTrigger : function(){
14194        
14195     },
14196
14197     // private
14198     onDestroy : function(){
14199         if(this.trigger){
14200             this.trigger.removeAllListeners();
14201           //  this.trigger.remove();
14202         }
14203         //if(this.wrap){
14204         //    this.wrap.remove();
14205         //}
14206         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14207     },
14208
14209     // private
14210     onFocus : function(){
14211         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14212         /*
14213         if(!this.mimicing){
14214             this.wrap.addClass('x-trigger-wrap-focus');
14215             this.mimicing = true;
14216             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14217             if(this.monitorTab){
14218                 this.el.on("keydown", this.checkTab, this);
14219             }
14220         }
14221         */
14222     },
14223
14224     // private
14225     checkTab : function(e){
14226         if(e.getKey() == e.TAB){
14227             this.triggerBlur();
14228         }
14229     },
14230
14231     // private
14232     onBlur : function(){
14233         // do nothing
14234     },
14235
14236     // private
14237     mimicBlur : function(e, t){
14238         /*
14239         if(!this.wrap.contains(t) && this.validateBlur()){
14240             this.triggerBlur();
14241         }
14242         */
14243     },
14244
14245     // private
14246     triggerBlur : function(){
14247         this.mimicing = false;
14248         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14249         if(this.monitorTab){
14250             this.el.un("keydown", this.checkTab, this);
14251         }
14252         //this.wrap.removeClass('x-trigger-wrap-focus');
14253         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14254     },
14255
14256     // private
14257     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14258     validateBlur : function(e, t){
14259         return true;
14260     },
14261
14262     // private
14263     onDisable : function(){
14264         this.inputEl().dom.disabled = true;
14265         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14266         //if(this.wrap){
14267         //    this.wrap.addClass('x-item-disabled');
14268         //}
14269     },
14270
14271     // private
14272     onEnable : function(){
14273         this.inputEl().dom.disabled = false;
14274         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14275         //if(this.wrap){
14276         //    this.el.removeClass('x-item-disabled');
14277         //}
14278     },
14279
14280     // private
14281     onShow : function(){
14282         var ae = this.getActionEl();
14283         
14284         if(ae){
14285             ae.dom.style.display = '';
14286             ae.dom.style.visibility = 'visible';
14287         }
14288     },
14289
14290     // private
14291     
14292     onHide : function(){
14293         var ae = this.getActionEl();
14294         ae.dom.style.display = 'none';
14295     },
14296
14297     /**
14298      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14299      * by an implementing function.
14300      * @method
14301      * @param {EventObject} e
14302      */
14303     onTriggerClick : Roo.emptyFn
14304 });
14305  
14306 /*
14307 * Licence: LGPL
14308 */
14309
14310 /**
14311  * @class Roo.bootstrap.form.CardUploader
14312  * @extends Roo.bootstrap.Button
14313  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14314  * @cfg {Number} errorTimeout default 3000
14315  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14316  * @cfg {Array}  html The button text.
14317
14318  *
14319  * @constructor
14320  * Create a new CardUploader
14321  * @param {Object} config The config object
14322  */
14323
14324 Roo.bootstrap.form.CardUploader = function(config){
14325     
14326  
14327     
14328     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14329     
14330     
14331     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14332         return r.data.id
14333      });
14334     
14335      this.addEvents({
14336          // raw events
14337         /**
14338          * @event preview
14339          * When a image is clicked on - and needs to display a slideshow or similar..
14340          * @param {Roo.bootstrap.Card} this
14341          * @param {Object} The image information data 
14342          *
14343          */
14344         'preview' : true,
14345          /**
14346          * @event download
14347          * When a the download link is clicked
14348          * @param {Roo.bootstrap.Card} this
14349          * @param {Object} The image information data  contains 
14350          */
14351         'download' : true
14352         
14353     });
14354 };
14355  
14356 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14357     
14358      
14359     errorTimeout : 3000,
14360      
14361     images : false,
14362    
14363     fileCollection : false,
14364     allowBlank : true,
14365     
14366     getAutoCreate : function()
14367     {
14368         
14369         var cfg =  {
14370             cls :'form-group' ,
14371             cn : [
14372                
14373                 {
14374                     tag: 'label',
14375                    //cls : 'input-group-addon',
14376                     html : this.fieldLabel
14377
14378                 },
14379
14380                 {
14381                     tag: 'input',
14382                     type : 'hidden',
14383                     name : this.name,
14384                     value : this.value,
14385                     cls : 'd-none  form-control'
14386                 },
14387                 
14388                 {
14389                     tag: 'input',
14390                     multiple : 'multiple',
14391                     type : 'file',
14392                     cls : 'd-none  roo-card-upload-selector'
14393                 },
14394                 
14395                 {
14396                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14397                 },
14398                 {
14399                     cls : 'card-columns roo-card-uploader-container'
14400                 }
14401
14402             ]
14403         };
14404            
14405          
14406         return cfg;
14407     },
14408     
14409     getChildContainer : function() /// what children are added to.
14410     {
14411         return this.containerEl;
14412     },
14413    
14414     getButtonContainer : function() /// what children are added to.
14415     {
14416         return this.el.select(".roo-card-uploader-button-container").first();
14417     },
14418    
14419     initEvents : function()
14420     {
14421         
14422         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14423         
14424         var t = this;
14425         this.addxtype({
14426             xns: Roo.bootstrap,
14427
14428             xtype : 'Button',
14429             container_method : 'getButtonContainer' ,            
14430             html :  this.html, // fix changable?
14431             cls : 'w-100 ',
14432             listeners : {
14433                 'click' : function(btn, e) {
14434                     t.onClick(e);
14435                 }
14436             }
14437         });
14438         
14439         
14440         
14441         
14442         this.urlAPI = (window.createObjectURL && window) || 
14443                                 (window.URL && URL.revokeObjectURL && URL) || 
14444                                 (window.webkitURL && webkitURL);
14445                         
14446          
14447          
14448          
14449         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14450         
14451         this.selectorEl.on('change', this.onFileSelected, this);
14452         if (this.images) {
14453             var t = this;
14454             this.images.forEach(function(img) {
14455                 t.addCard(img)
14456             });
14457             this.images = false;
14458         }
14459         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14460          
14461        
14462     },
14463     
14464    
14465     onClick : function(e)
14466     {
14467         e.preventDefault();
14468          
14469         this.selectorEl.dom.click();
14470          
14471     },
14472     
14473     onFileSelected : function(e)
14474     {
14475         e.preventDefault();
14476         
14477         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14478             return;
14479         }
14480         
14481         Roo.each(this.selectorEl.dom.files, function(file){    
14482             this.addFile(file);
14483         }, this);
14484          
14485     },
14486     
14487       
14488     
14489       
14490     
14491     addFile : function(file)
14492     {
14493            
14494         if(typeof(file) === 'string'){
14495             throw "Add file by name?"; // should not happen
14496             return;
14497         }
14498         
14499         if(!file || !this.urlAPI){
14500             return;
14501         }
14502         
14503         // file;
14504         // file.type;
14505         
14506         var _this = this;
14507         
14508         
14509         var url = _this.urlAPI.createObjectURL( file);
14510            
14511         this.addCard({
14512             id : Roo.bootstrap.form.CardUploader.ID--,
14513             is_uploaded : false,
14514             src : url,
14515             srcfile : file,
14516             title : file.name,
14517             mimetype : file.type,
14518             preview : false,
14519             is_deleted : 0
14520         });
14521         
14522     },
14523     
14524     /**
14525      * addCard - add an Attachment to the uploader
14526      * @param data - the data about the image to upload
14527      *
14528      * {
14529           id : 123
14530           title : "Title of file",
14531           is_uploaded : false,
14532           src : "http://.....",
14533           srcfile : { the File upload object },
14534           mimetype : file.type,
14535           preview : false,
14536           is_deleted : 0
14537           .. any other data...
14538         }
14539      *
14540      * 
14541     */
14542     
14543     addCard : function (data)
14544     {
14545         // hidden input element?
14546         // if the file is not an image...
14547         //then we need to use something other that and header_image
14548         var t = this;
14549         //   remove.....
14550         var footer = [
14551             {
14552                 xns : Roo.bootstrap,
14553                 xtype : 'CardFooter',
14554                  items: [
14555                     {
14556                         xns : Roo.bootstrap,
14557                         xtype : 'Element',
14558                         cls : 'd-flex',
14559                         items : [
14560                             
14561                             {
14562                                 xns : Roo.bootstrap,
14563                                 xtype : 'Button',
14564                                 html : String.format("<small>{0}</small>", data.title),
14565                                 cls : 'col-10 text-left',
14566                                 size: 'sm',
14567                                 weight: 'link',
14568                                 fa : 'download',
14569                                 listeners : {
14570                                     click : function() {
14571                                      
14572                                         t.fireEvent( "download", t, data );
14573                                     }
14574                                 }
14575                             },
14576                           
14577                             {
14578                                 xns : Roo.bootstrap,
14579                                 xtype : 'Button',
14580                                 style: 'max-height: 28px; ',
14581                                 size : 'sm',
14582                                 weight: 'danger',
14583                                 cls : 'col-2',
14584                                 fa : 'times',
14585                                 listeners : {
14586                                     click : function() {
14587                                         t.removeCard(data.id)
14588                                     }
14589                                 }
14590                             }
14591                         ]
14592                     }
14593                     
14594                 ] 
14595             }
14596             
14597         ];
14598         
14599         var cn = this.addxtype(
14600             {
14601                  
14602                 xns : Roo.bootstrap,
14603                 xtype : 'Card',
14604                 closeable : true,
14605                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14606                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14607                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14608                 data : data,
14609                 html : false,
14610                  
14611                 items : footer,
14612                 initEvents : function() {
14613                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14614                     var card = this;
14615                     this.imgEl = this.el.select('.card-img-top').first();
14616                     if (this.imgEl) {
14617                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14618                         this.imgEl.set({ 'pointer' : 'cursor' });
14619                                   
14620                     }
14621                     this.getCardFooter().addClass('p-1');
14622                     
14623                   
14624                 }
14625                 
14626             }
14627         );
14628         // dont' really need ot update items.
14629         // this.items.push(cn);
14630         this.fileCollection.add(cn);
14631         
14632         if (!data.srcfile) {
14633             this.updateInput();
14634             return;
14635         }
14636             
14637         var _t = this;
14638         var reader = new FileReader();
14639         reader.addEventListener("load", function() {  
14640             data.srcdata =  reader.result;
14641             _t.updateInput();
14642         });
14643         reader.readAsDataURL(data.srcfile);
14644         
14645         
14646         
14647     },
14648     removeCard : function(id)
14649     {
14650         
14651         var card  = this.fileCollection.get(id);
14652         card.data.is_deleted = 1;
14653         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14654         //this.fileCollection.remove(card);
14655         //this.items = this.items.filter(function(e) { return e != card });
14656         // dont' really need ot update items.
14657         card.el.dom.parentNode.removeChild(card.el.dom);
14658         this.updateInput();
14659
14660         
14661     },
14662     reset: function()
14663     {
14664         this.fileCollection.each(function(card) {
14665             if (card.el.dom && card.el.dom.parentNode) {
14666                 card.el.dom.parentNode.removeChild(card.el.dom);
14667             }
14668         });
14669         this.fileCollection.clear();
14670         this.updateInput();
14671     },
14672     
14673     updateInput : function()
14674     {
14675          var data = [];
14676         this.fileCollection.each(function(e) {
14677             data.push(e.data);
14678             
14679         });
14680         this.inputEl().dom.value = JSON.stringify(data);
14681         
14682         
14683         
14684     }
14685     
14686     
14687 });
14688
14689
14690 Roo.bootstrap.form.CardUploader.ID = -1;/*
14691  * Based on:
14692  * Ext JS Library 1.1.1
14693  * Copyright(c) 2006-2007, Ext JS, LLC.
14694  *
14695  * Originally Released Under LGPL - original licence link has changed is not relivant.
14696  *
14697  * Fork - LGPL
14698  * <script type="text/javascript">
14699  */
14700
14701
14702 /**
14703  * @class Roo.data.SortTypes
14704  * @static
14705  * Defines the default sorting (casting?) comparison functions used when sorting data.
14706  */
14707 Roo.data.SortTypes = {
14708     /**
14709      * Default sort that does nothing
14710      * @param {Mixed} s The value being converted
14711      * @return {Mixed} The comparison value
14712      */
14713     none : function(s){
14714         return s;
14715     },
14716     
14717     /**
14718      * The regular expression used to strip tags
14719      * @type {RegExp}
14720      * @property
14721      */
14722     stripTagsRE : /<\/?[^>]+>/gi,
14723     
14724     /**
14725      * Strips all HTML tags to sort on text only
14726      * @param {Mixed} s The value being converted
14727      * @return {String} The comparison value
14728      */
14729     asText : function(s){
14730         return String(s).replace(this.stripTagsRE, "");
14731     },
14732     
14733     /**
14734      * Strips all HTML tags to sort on text only - Case insensitive
14735      * @param {Mixed} s The value being converted
14736      * @return {String} The comparison value
14737      */
14738     asUCText : function(s){
14739         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14740     },
14741     
14742     /**
14743      * Case insensitive string
14744      * @param {Mixed} s The value being converted
14745      * @return {String} The comparison value
14746      */
14747     asUCString : function(s) {
14748         return String(s).toUpperCase();
14749     },
14750     
14751     /**
14752      * Date sorting
14753      * @param {Mixed} s The value being converted
14754      * @return {Number} The comparison value
14755      */
14756     asDate : function(s) {
14757         if(!s){
14758             return 0;
14759         }
14760         if(s instanceof Date){
14761             return s.getTime();
14762         }
14763         return Date.parse(String(s));
14764     },
14765     
14766     /**
14767      * Float sorting
14768      * @param {Mixed} s The value being converted
14769      * @return {Float} The comparison value
14770      */
14771     asFloat : function(s) {
14772         var val = parseFloat(String(s).replace(/,/g, ""));
14773         if(isNaN(val)) {
14774             val = 0;
14775         }
14776         return val;
14777     },
14778     
14779     /**
14780      * Integer sorting
14781      * @param {Mixed} s The value being converted
14782      * @return {Number} The comparison value
14783      */
14784     asInt : function(s) {
14785         var val = parseInt(String(s).replace(/,/g, ""));
14786         if(isNaN(val)) {
14787             val = 0;
14788         }
14789         return val;
14790     }
14791 };/*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801
14802 /**
14803 * @class Roo.data.Record
14804  * Instances of this class encapsulate both record <em>definition</em> information, and record
14805  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14806  * to access Records cached in an {@link Roo.data.Store} object.<br>
14807  * <p>
14808  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14809  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14810  * objects.<br>
14811  * <p>
14812  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14813  * @constructor
14814  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14815  * {@link #create}. The parameters are the same.
14816  * @param {Array} data An associative Array of data values keyed by the field name.
14817  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14818  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14819  * not specified an integer id is generated.
14820  */
14821 Roo.data.Record = function(data, id){
14822     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14823     this.data = data;
14824 };
14825
14826 /**
14827  * Generate a constructor for a specific record layout.
14828  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14829  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14830  * Each field definition object may contain the following properties: <ul>
14831  * <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,
14832  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14833  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14834  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14835  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14836  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14837  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14838  * this may be omitted.</p></li>
14839  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14840  * <ul><li>auto (Default, implies no conversion)</li>
14841  * <li>string</li>
14842  * <li>int</li>
14843  * <li>float</li>
14844  * <li>boolean</li>
14845  * <li>date</li></ul></p></li>
14846  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14847  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14848  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14849  * by the Reader into an object that will be stored in the Record. It is passed the
14850  * following parameters:<ul>
14851  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14852  * </ul></p></li>
14853  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14854  * </ul>
14855  * <br>usage:<br><pre><code>
14856 var TopicRecord = Roo.data.Record.create(
14857     {name: 'title', mapping: 'topic_title'},
14858     {name: 'author', mapping: 'username'},
14859     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14860     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14861     {name: 'lastPoster', mapping: 'user2'},
14862     {name: 'excerpt', mapping: 'post_text'}
14863 );
14864
14865 var myNewRecord = new TopicRecord({
14866     title: 'Do my job please',
14867     author: 'noobie',
14868     totalPosts: 1,
14869     lastPost: new Date(),
14870     lastPoster: 'Animal',
14871     excerpt: 'No way dude!'
14872 });
14873 myStore.add(myNewRecord);
14874 </code></pre>
14875  * @method create
14876  * @static
14877  */
14878 Roo.data.Record.create = function(o){
14879     var f = function(){
14880         f.superclass.constructor.apply(this, arguments);
14881     };
14882     Roo.extend(f, Roo.data.Record);
14883     var p = f.prototype;
14884     p.fields = new Roo.util.MixedCollection(false, function(field){
14885         return field.name;
14886     });
14887     for(var i = 0, len = o.length; i < len; i++){
14888         p.fields.add(new Roo.data.Field(o[i]));
14889     }
14890     f.getField = function(name){
14891         return p.fields.get(name);  
14892     };
14893     return f;
14894 };
14895
14896 Roo.data.Record.AUTO_ID = 1000;
14897 Roo.data.Record.EDIT = 'edit';
14898 Roo.data.Record.REJECT = 'reject';
14899 Roo.data.Record.COMMIT = 'commit';
14900
14901 Roo.data.Record.prototype = {
14902     /**
14903      * Readonly flag - true if this record has been modified.
14904      * @type Boolean
14905      */
14906     dirty : false,
14907     editing : false,
14908     error: null,
14909     modified: null,
14910
14911     // private
14912     join : function(store){
14913         this.store = store;
14914     },
14915
14916     /**
14917      * Set the named field to the specified value.
14918      * @param {String} name The name of the field to set.
14919      * @param {Object} value The value to set the field to.
14920      */
14921     set : function(name, value){
14922         if(this.data[name] == value){
14923             return;
14924         }
14925         this.dirty = true;
14926         if(!this.modified){
14927             this.modified = {};
14928         }
14929         if(typeof this.modified[name] == 'undefined'){
14930             this.modified[name] = this.data[name];
14931         }
14932         this.data[name] = value;
14933         if(!this.editing && this.store){
14934             this.store.afterEdit(this);
14935         }       
14936     },
14937
14938     /**
14939      * Get the value of the named field.
14940      * @param {String} name The name of the field to get the value of.
14941      * @return {Object} The value of the field.
14942      */
14943     get : function(name){
14944         return this.data[name]; 
14945     },
14946
14947     // private
14948     beginEdit : function(){
14949         this.editing = true;
14950         this.modified = {}; 
14951     },
14952
14953     // private
14954     cancelEdit : function(){
14955         this.editing = false;
14956         delete this.modified;
14957     },
14958
14959     // private
14960     endEdit : function(){
14961         this.editing = false;
14962         if(this.dirty && this.store){
14963             this.store.afterEdit(this);
14964         }
14965     },
14966
14967     /**
14968      * Usually called by the {@link Roo.data.Store} which owns the Record.
14969      * Rejects all changes made to the Record since either creation, or the last commit operation.
14970      * Modified fields are reverted to their original values.
14971      * <p>
14972      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14973      * of reject operations.
14974      */
14975     reject : function(){
14976         var m = this.modified;
14977         for(var n in m){
14978             if(typeof m[n] != "function"){
14979                 this.data[n] = m[n];
14980             }
14981         }
14982         this.dirty = false;
14983         delete this.modified;
14984         this.editing = false;
14985         if(this.store){
14986             this.store.afterReject(this);
14987         }
14988     },
14989
14990     /**
14991      * Usually called by the {@link Roo.data.Store} which owns the Record.
14992      * Commits all changes made to the Record since either creation, or the last commit operation.
14993      * <p>
14994      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14995      * of commit operations.
14996      */
14997     commit : function(){
14998         this.dirty = false;
14999         delete this.modified;
15000         this.editing = false;
15001         if(this.store){
15002             this.store.afterCommit(this);
15003         }
15004     },
15005
15006     // private
15007     hasError : function(){
15008         return this.error != null;
15009     },
15010
15011     // private
15012     clearError : function(){
15013         this.error = null;
15014     },
15015
15016     /**
15017      * Creates a copy of this record.
15018      * @param {String} id (optional) A new record id if you don't want to use this record's id
15019      * @return {Record}
15020      */
15021     copy : function(newId) {
15022         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15023     }
15024 };/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034
15035
15036
15037 /**
15038  * @class Roo.data.Store
15039  * @extends Roo.util.Observable
15040  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15041  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15042  * <p>
15043  * 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
15044  * has no knowledge of the format of the data returned by the Proxy.<br>
15045  * <p>
15046  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15047  * instances from the data object. These records are cached and made available through accessor functions.
15048  * @constructor
15049  * Creates a new Store.
15050  * @param {Object} config A config object containing the objects needed for the Store to access data,
15051  * and read the data into Records.
15052  */
15053 Roo.data.Store = function(config){
15054     this.data = new Roo.util.MixedCollection(false);
15055     this.data.getKey = function(o){
15056         return o.id;
15057     };
15058     this.baseParams = {};
15059     // private
15060     this.paramNames = {
15061         "start" : "start",
15062         "limit" : "limit",
15063         "sort" : "sort",
15064         "dir" : "dir",
15065         "multisort" : "_multisort"
15066     };
15067
15068     if(config && config.data){
15069         this.inlineData = config.data;
15070         delete config.data;
15071     }
15072
15073     Roo.apply(this, config);
15074     
15075     if(this.reader){ // reader passed
15076         this.reader = Roo.factory(this.reader, Roo.data);
15077         this.reader.xmodule = this.xmodule || false;
15078         if(!this.recordType){
15079             this.recordType = this.reader.recordType;
15080         }
15081         if(this.reader.onMetaChange){
15082             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15083         }
15084     }
15085
15086     if(this.recordType){
15087         this.fields = this.recordType.prototype.fields;
15088     }
15089     this.modified = [];
15090
15091     this.addEvents({
15092         /**
15093          * @event datachanged
15094          * Fires when the data cache has changed, and a widget which is using this Store
15095          * as a Record cache should refresh its view.
15096          * @param {Store} this
15097          */
15098         datachanged : true,
15099         /**
15100          * @event metachange
15101          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15102          * @param {Store} this
15103          * @param {Object} meta The JSON metadata
15104          */
15105         metachange : true,
15106         /**
15107          * @event add
15108          * Fires when Records have been added to the Store
15109          * @param {Store} this
15110          * @param {Roo.data.Record[]} records The array of Records added
15111          * @param {Number} index The index at which the record(s) were added
15112          */
15113         add : true,
15114         /**
15115          * @event remove
15116          * Fires when a Record has been removed from the Store
15117          * @param {Store} this
15118          * @param {Roo.data.Record} record The Record that was removed
15119          * @param {Number} index The index at which the record was removed
15120          */
15121         remove : true,
15122         /**
15123          * @event update
15124          * Fires when a Record has been updated
15125          * @param {Store} this
15126          * @param {Roo.data.Record} record The Record that was updated
15127          * @param {String} operation The update operation being performed.  Value may be one of:
15128          * <pre><code>
15129  Roo.data.Record.EDIT
15130  Roo.data.Record.REJECT
15131  Roo.data.Record.COMMIT
15132          * </code></pre>
15133          */
15134         update : true,
15135         /**
15136          * @event clear
15137          * Fires when the data cache has been cleared.
15138          * @param {Store} this
15139          */
15140         clear : true,
15141         /**
15142          * @event beforeload
15143          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15144          * the load action will be canceled.
15145          * @param {Store} this
15146          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147          */
15148         beforeload : true,
15149         /**
15150          * @event beforeloadadd
15151          * Fires after a new set of Records has been loaded.
15152          * @param {Store} this
15153          * @param {Roo.data.Record[]} records The Records that were loaded
15154          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155          */
15156         beforeloadadd : true,
15157         /**
15158          * @event load
15159          * Fires after a new set of Records has been loaded, before they are added to the store.
15160          * @param {Store} this
15161          * @param {Roo.data.Record[]} records The Records that were loaded
15162          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15163          * @params {Object} return from reader
15164          */
15165         load : true,
15166         /**
15167          * @event loadexception
15168          * Fires if an exception occurs in the Proxy during loading.
15169          * Called with the signature of the Proxy's "loadexception" event.
15170          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15171          * 
15172          * @param {Proxy} 
15173          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15174          * @param {Object} load options 
15175          * @param {Object} jsonData from your request (normally this contains the Exception)
15176          */
15177         loadexception : true
15178     });
15179     
15180     if(this.proxy){
15181         this.proxy = Roo.factory(this.proxy, Roo.data);
15182         this.proxy.xmodule = this.xmodule || false;
15183         this.relayEvents(this.proxy,  ["loadexception"]);
15184     }
15185     this.sortToggle = {};
15186     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15187
15188     Roo.data.Store.superclass.constructor.call(this);
15189
15190     if(this.inlineData){
15191         this.loadData(this.inlineData);
15192         delete this.inlineData;
15193     }
15194 };
15195
15196 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15197      /**
15198     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15199     * without a remote query - used by combo/forms at present.
15200     */
15201     
15202     /**
15203     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15204     */
15205     /**
15206     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15207     */
15208     /**
15209     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15210     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15211     */
15212     /**
15213     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15214     * on any HTTP request
15215     */
15216     /**
15217     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15218     */
15219     /**
15220     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15221     */
15222     multiSort: false,
15223     /**
15224     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15225     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15226     */
15227     remoteSort : false,
15228
15229     /**
15230     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15231      * loaded or when a record is removed. (defaults to false).
15232     */
15233     pruneModifiedRecords : false,
15234
15235     // private
15236     lastOptions : null,
15237
15238     /**
15239      * Add Records to the Store and fires the add event.
15240      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15241      */
15242     add : function(records){
15243         records = [].concat(records);
15244         for(var i = 0, len = records.length; i < len; i++){
15245             records[i].join(this);
15246         }
15247         var index = this.data.length;
15248         this.data.addAll(records);
15249         this.fireEvent("add", this, records, index);
15250     },
15251
15252     /**
15253      * Remove a Record from the Store and fires the remove event.
15254      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15255      */
15256     remove : function(record){
15257         var index = this.data.indexOf(record);
15258         this.data.removeAt(index);
15259  
15260         if(this.pruneModifiedRecords){
15261             this.modified.remove(record);
15262         }
15263         this.fireEvent("remove", this, record, index);
15264     },
15265
15266     /**
15267      * Remove all Records from the Store and fires the clear event.
15268      */
15269     removeAll : function(){
15270         this.data.clear();
15271         if(this.pruneModifiedRecords){
15272             this.modified = [];
15273         }
15274         this.fireEvent("clear", this);
15275     },
15276
15277     /**
15278      * Inserts Records to the Store at the given index and fires the add event.
15279      * @param {Number} index The start index at which to insert the passed Records.
15280      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15281      */
15282     insert : function(index, records){
15283         records = [].concat(records);
15284         for(var i = 0, len = records.length; i < len; i++){
15285             this.data.insert(index, records[i]);
15286             records[i].join(this);
15287         }
15288         this.fireEvent("add", this, records, index);
15289     },
15290
15291     /**
15292      * Get the index within the cache of the passed Record.
15293      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15294      * @return {Number} The index of the passed Record. Returns -1 if not found.
15295      */
15296     indexOf : function(record){
15297         return this.data.indexOf(record);
15298     },
15299
15300     /**
15301      * Get the index within the cache of the Record with the passed id.
15302      * @param {String} id The id of the Record to find.
15303      * @return {Number} The index of the Record. Returns -1 if not found.
15304      */
15305     indexOfId : function(id){
15306         return this.data.indexOfKey(id);
15307     },
15308
15309     /**
15310      * Get the Record with the specified id.
15311      * @param {String} id The id of the Record to find.
15312      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15313      */
15314     getById : function(id){
15315         return this.data.key(id);
15316     },
15317
15318     /**
15319      * Get the Record at the specified index.
15320      * @param {Number} index The index of the Record to find.
15321      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15322      */
15323     getAt : function(index){
15324         return this.data.itemAt(index);
15325     },
15326
15327     /**
15328      * Returns a range of Records between specified indices.
15329      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15330      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15331      * @return {Roo.data.Record[]} An array of Records
15332      */
15333     getRange : function(start, end){
15334         return this.data.getRange(start, end);
15335     },
15336
15337     // private
15338     storeOptions : function(o){
15339         o = Roo.apply({}, o);
15340         delete o.callback;
15341         delete o.scope;
15342         this.lastOptions = o;
15343     },
15344
15345     /**
15346      * Loads the Record cache from the configured Proxy using the configured Reader.
15347      * <p>
15348      * If using remote paging, then the first load call must specify the <em>start</em>
15349      * and <em>limit</em> properties in the options.params property to establish the initial
15350      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15351      * <p>
15352      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15353      * and this call will return before the new data has been loaded. Perform any post-processing
15354      * in a callback function, or in a "load" event handler.</strong>
15355      * <p>
15356      * @param {Object} options An object containing properties which control loading options:<ul>
15357      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15358      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15359      * <pre>
15360                 {
15361                     data : data,  // array of key=>value data like JsonReader
15362                     total : data.length,
15363                     success : true
15364                     
15365                 }
15366         </pre>
15367             }.</li>
15368      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15369      * passed the following arguments:<ul>
15370      * <li>r : Roo.data.Record[]</li>
15371      * <li>options: Options object from the load call</li>
15372      * <li>success: Boolean success indicator</li></ul></li>
15373      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15374      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15375      * </ul>
15376      */
15377     load : function(options){
15378         options = options || {};
15379         if(this.fireEvent("beforeload", this, options) !== false){
15380             this.storeOptions(options);
15381             var p = Roo.apply(options.params || {}, this.baseParams);
15382             // if meta was not loaded from remote source.. try requesting it.
15383             if (!this.reader.metaFromRemote) {
15384                 p._requestMeta = 1;
15385             }
15386             if(this.sortInfo && this.remoteSort){
15387                 var pn = this.paramNames;
15388                 p[pn["sort"]] = this.sortInfo.field;
15389                 p[pn["dir"]] = this.sortInfo.direction;
15390             }
15391             if (this.multiSort) {
15392                 var pn = this.paramNames;
15393                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15394             }
15395             
15396             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15397         }
15398     },
15399
15400     /**
15401      * Reloads the Record cache from the configured Proxy using the configured Reader and
15402      * the options from the last load operation performed.
15403      * @param {Object} options (optional) An object containing properties which may override the options
15404      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15405      * the most recently used options are reused).
15406      */
15407     reload : function(options){
15408         this.load(Roo.applyIf(options||{}, this.lastOptions));
15409     },
15410
15411     // private
15412     // Called as a callback by the Reader during a load operation.
15413     loadRecords : function(o, options, success){
15414          
15415         if(!o){
15416             if(success !== false){
15417                 this.fireEvent("load", this, [], options, o);
15418             }
15419             if(options.callback){
15420                 options.callback.call(options.scope || this, [], options, false);
15421             }
15422             return;
15423         }
15424         // if data returned failure - throw an exception.
15425         if (o.success === false) {
15426             // show a message if no listener is registered.
15427             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15428                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15429             }
15430             // loadmask wil be hooked into this..
15431             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15432             return;
15433         }
15434         var r = o.records, t = o.totalRecords || r.length;
15435         
15436         this.fireEvent("beforeloadadd", this, r, options, o);
15437         
15438         if(!options || options.add !== true){
15439             if(this.pruneModifiedRecords){
15440                 this.modified = [];
15441             }
15442             for(var i = 0, len = r.length; i < len; i++){
15443                 r[i].join(this);
15444             }
15445             if(this.snapshot){
15446                 this.data = this.snapshot;
15447                 delete this.snapshot;
15448             }
15449             this.data.clear();
15450             this.data.addAll(r);
15451             this.totalLength = t;
15452             this.applySort();
15453             this.fireEvent("datachanged", this);
15454         }else{
15455             this.totalLength = Math.max(t, this.data.length+r.length);
15456             this.add(r);
15457         }
15458         
15459         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15460                 
15461             var e = new Roo.data.Record({});
15462
15463             e.set(this.parent.displayField, this.parent.emptyTitle);
15464             e.set(this.parent.valueField, '');
15465
15466             this.insert(0, e);
15467         }
15468             
15469         this.fireEvent("load", this, r, options, o);
15470         if(options.callback){
15471             options.callback.call(options.scope || this, r, options, true);
15472         }
15473     },
15474
15475
15476     /**
15477      * Loads data from a passed data block. A Reader which understands the format of the data
15478      * must have been configured in the constructor.
15479      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15480      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15481      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15482      */
15483     loadData : function(o, append){
15484         var r = this.reader.readRecords(o);
15485         this.loadRecords(r, {add: append}, true);
15486     },
15487     
15488      /**
15489      * using 'cn' the nested child reader read the child array into it's child stores.
15490      * @param {Object} rec The record with a 'children array
15491      */
15492     loadDataFromChildren : function(rec)
15493     {
15494         this.loadData(this.reader.toLoadData(rec));
15495     },
15496     
15497
15498     /**
15499      * Gets the number of cached records.
15500      * <p>
15501      * <em>If using paging, this may not be the total size of the dataset. If the data object
15502      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15503      * the data set size</em>
15504      */
15505     getCount : function(){
15506         return this.data.length || 0;
15507     },
15508
15509     /**
15510      * Gets the total number of records in the dataset as returned by the server.
15511      * <p>
15512      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15513      * the dataset size</em>
15514      */
15515     getTotalCount : function(){
15516         return this.totalLength || 0;
15517     },
15518
15519     /**
15520      * Returns the sort state of the Store as an object with two properties:
15521      * <pre><code>
15522  field {String} The name of the field by which the Records are sorted
15523  direction {String} The sort order, "ASC" or "DESC"
15524      * </code></pre>
15525      */
15526     getSortState : function(){
15527         return this.sortInfo;
15528     },
15529
15530     // private
15531     applySort : function(){
15532         if(this.sortInfo && !this.remoteSort){
15533             var s = this.sortInfo, f = s.field;
15534             var st = this.fields.get(f).sortType;
15535             var fn = function(r1, r2){
15536                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15537                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15538             };
15539             this.data.sort(s.direction, fn);
15540             if(this.snapshot && this.snapshot != this.data){
15541                 this.snapshot.sort(s.direction, fn);
15542             }
15543         }
15544     },
15545
15546     /**
15547      * Sets the default sort column and order to be used by the next load operation.
15548      * @param {String} fieldName The name of the field to sort by.
15549      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15550      */
15551     setDefaultSort : function(field, dir){
15552         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15553     },
15554
15555     /**
15556      * Sort the Records.
15557      * If remote sorting is used, the sort is performed on the server, and the cache is
15558      * reloaded. If local sorting is used, the cache is sorted internally.
15559      * @param {String} fieldName The name of the field to sort by.
15560      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15561      */
15562     sort : function(fieldName, dir){
15563         var f = this.fields.get(fieldName);
15564         if(!dir){
15565             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15566             
15567             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15568                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15569             }else{
15570                 dir = f.sortDir;
15571             }
15572         }
15573         this.sortToggle[f.name] = dir;
15574         this.sortInfo = {field: f.name, direction: dir};
15575         if(!this.remoteSort){
15576             this.applySort();
15577             this.fireEvent("datachanged", this);
15578         }else{
15579             this.load(this.lastOptions);
15580         }
15581     },
15582
15583     /**
15584      * Calls the specified function for each of the Records in the cache.
15585      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15586      * Returning <em>false</em> aborts and exits the iteration.
15587      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15588      */
15589     each : function(fn, scope){
15590         this.data.each(fn, scope);
15591     },
15592
15593     /**
15594      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15595      * (e.g., during paging).
15596      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15597      */
15598     getModifiedRecords : function(){
15599         return this.modified;
15600     },
15601
15602     // private
15603     createFilterFn : function(property, value, anyMatch){
15604         if(!value.exec){ // not a regex
15605             value = String(value);
15606             if(value.length == 0){
15607                 return false;
15608             }
15609             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15610         }
15611         return function(r){
15612             return value.test(r.data[property]);
15613         };
15614     },
15615
15616     /**
15617      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15618      * @param {String} property A field on your records
15619      * @param {Number} start The record index to start at (defaults to 0)
15620      * @param {Number} end The last record index to include (defaults to length - 1)
15621      * @return {Number} The sum
15622      */
15623     sum : function(property, start, end){
15624         var rs = this.data.items, v = 0;
15625         start = start || 0;
15626         end = (end || end === 0) ? end : rs.length-1;
15627
15628         for(var i = start; i <= end; i++){
15629             v += (rs[i].data[property] || 0);
15630         }
15631         return v;
15632     },
15633
15634     /**
15635      * Filter the records by a specified property.
15636      * @param {String} field A field on your records
15637      * @param {String/RegExp} value Either a string that the field
15638      * should start with or a RegExp to test against the field
15639      * @param {Boolean} anyMatch True to match any part not just the beginning
15640      */
15641     filter : function(property, value, anyMatch){
15642         var fn = this.createFilterFn(property, value, anyMatch);
15643         return fn ? this.filterBy(fn) : this.clearFilter();
15644     },
15645
15646     /**
15647      * Filter by a function. The specified function will be called with each
15648      * record in this data source. If the function returns true the record is included,
15649      * otherwise it is filtered.
15650      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15651      * @param {Object} scope (optional) The scope of the function (defaults to this)
15652      */
15653     filterBy : function(fn, scope){
15654         this.snapshot = this.snapshot || this.data;
15655         this.data = this.queryBy(fn, scope||this);
15656         this.fireEvent("datachanged", this);
15657     },
15658
15659     /**
15660      * Query the records by a specified property.
15661      * @param {String} field A field on your records
15662      * @param {String/RegExp} value Either a string that the field
15663      * should start with or a RegExp to test against the field
15664      * @param {Boolean} anyMatch True to match any part not just the beginning
15665      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15666      */
15667     query : function(property, value, anyMatch){
15668         var fn = this.createFilterFn(property, value, anyMatch);
15669         return fn ? this.queryBy(fn) : this.data.clone();
15670     },
15671
15672     /**
15673      * Query by a function. The specified function will be called with each
15674      * record in this data source. If the function returns true the record is included
15675      * in the results.
15676      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15677      * @param {Object} scope (optional) The scope of the function (defaults to this)
15678       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15679      **/
15680     queryBy : function(fn, scope){
15681         var data = this.snapshot || this.data;
15682         return data.filterBy(fn, scope||this);
15683     },
15684
15685     /**
15686      * Collects unique values for a particular dataIndex from this store.
15687      * @param {String} dataIndex The property to collect
15688      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15689      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15690      * @return {Array} An array of the unique values
15691      **/
15692     collect : function(dataIndex, allowNull, bypassFilter){
15693         var d = (bypassFilter === true && this.snapshot) ?
15694                 this.snapshot.items : this.data.items;
15695         var v, sv, r = [], l = {};
15696         for(var i = 0, len = d.length; i < len; i++){
15697             v = d[i].data[dataIndex];
15698             sv = String(v);
15699             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15700                 l[sv] = true;
15701                 r[r.length] = v;
15702             }
15703         }
15704         return r;
15705     },
15706
15707     /**
15708      * Revert to a view of the Record cache with no filtering applied.
15709      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15710      */
15711     clearFilter : function(suppressEvent){
15712         if(this.snapshot && this.snapshot != this.data){
15713             this.data = this.snapshot;
15714             delete this.snapshot;
15715             if(suppressEvent !== true){
15716                 this.fireEvent("datachanged", this);
15717             }
15718         }
15719     },
15720
15721     // private
15722     afterEdit : function(record){
15723         if(this.modified.indexOf(record) == -1){
15724             this.modified.push(record);
15725         }
15726         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15727     },
15728     
15729     // private
15730     afterReject : function(record){
15731         this.modified.remove(record);
15732         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15733     },
15734
15735     // private
15736     afterCommit : function(record){
15737         this.modified.remove(record);
15738         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15739     },
15740
15741     /**
15742      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15743      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15744      */
15745     commitChanges : function(){
15746         var m = this.modified.slice(0);
15747         this.modified = [];
15748         for(var i = 0, len = m.length; i < len; i++){
15749             m[i].commit();
15750         }
15751     },
15752
15753     /**
15754      * Cancel outstanding changes on all changed records.
15755      */
15756     rejectChanges : function(){
15757         var m = this.modified.slice(0);
15758         this.modified = [];
15759         for(var i = 0, len = m.length; i < len; i++){
15760             m[i].reject();
15761         }
15762     },
15763
15764     onMetaChange : function(meta, rtype, o){
15765         this.recordType = rtype;
15766         this.fields = rtype.prototype.fields;
15767         delete this.snapshot;
15768         this.sortInfo = meta.sortInfo || this.sortInfo;
15769         this.modified = [];
15770         this.fireEvent('metachange', this, this.reader.meta);
15771     },
15772     
15773     moveIndex : function(data, type)
15774     {
15775         var index = this.indexOf(data);
15776         
15777         var newIndex = index + type;
15778         
15779         this.remove(data);
15780         
15781         this.insert(newIndex, data);
15782         
15783     }
15784 });/*
15785  * Based on:
15786  * Ext JS Library 1.1.1
15787  * Copyright(c) 2006-2007, Ext JS, LLC.
15788  *
15789  * Originally Released Under LGPL - original licence link has changed is not relivant.
15790  *
15791  * Fork - LGPL
15792  * <script type="text/javascript">
15793  */
15794
15795 /**
15796  * @class Roo.data.SimpleStore
15797  * @extends Roo.data.Store
15798  * Small helper class to make creating Stores from Array data easier.
15799  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15800  * @cfg {Array} fields An array of field definition objects, or field name strings.
15801  * @cfg {Object} an existing reader (eg. copied from another store)
15802  * @cfg {Array} data The multi-dimensional array of data
15803  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15804  * @cfg {Roo.data.Reader} reader  [not-required] 
15805  * @constructor
15806  * @param {Object} config
15807  */
15808 Roo.data.SimpleStore = function(config)
15809 {
15810     Roo.data.SimpleStore.superclass.constructor.call(this, {
15811         isLocal : true,
15812         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15813                 id: config.id
15814             },
15815             Roo.data.Record.create(config.fields)
15816         ),
15817         proxy : new Roo.data.MemoryProxy(config.data)
15818     });
15819     this.load();
15820 };
15821 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15822  * Based on:
15823  * Ext JS Library 1.1.1
15824  * Copyright(c) 2006-2007, Ext JS, LLC.
15825  *
15826  * Originally Released Under LGPL - original licence link has changed is not relivant.
15827  *
15828  * Fork - LGPL
15829  * <script type="text/javascript">
15830  */
15831
15832 /**
15833 /**
15834  * @extends Roo.data.Store
15835  * @class Roo.data.JsonStore
15836  * Small helper class to make creating Stores for JSON data easier. <br/>
15837 <pre><code>
15838 var store = new Roo.data.JsonStore({
15839     url: 'get-images.php',
15840     root: 'images',
15841     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15842 });
15843 </code></pre>
15844  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15845  * JsonReader and HttpProxy (unless inline data is provided).</b>
15846  * @cfg {Array} fields An array of field definition objects, or field name strings.
15847  * @constructor
15848  * @param {Object} config
15849  */
15850 Roo.data.JsonStore = function(c){
15851     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15852         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15853         reader: new Roo.data.JsonReader(c, c.fields)
15854     }));
15855 };
15856 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15857  * Based on:
15858  * Ext JS Library 1.1.1
15859  * Copyright(c) 2006-2007, Ext JS, LLC.
15860  *
15861  * Originally Released Under LGPL - original licence link has changed is not relivant.
15862  *
15863  * Fork - LGPL
15864  * <script type="text/javascript">
15865  */
15866
15867  
15868 Roo.data.Field = function(config){
15869     if(typeof config == "string"){
15870         config = {name: config};
15871     }
15872     Roo.apply(this, config);
15873     
15874     if(!this.type){
15875         this.type = "auto";
15876     }
15877     
15878     var st = Roo.data.SortTypes;
15879     // named sortTypes are supported, here we look them up
15880     if(typeof this.sortType == "string"){
15881         this.sortType = st[this.sortType];
15882     }
15883     
15884     // set default sortType for strings and dates
15885     if(!this.sortType){
15886         switch(this.type){
15887             case "string":
15888                 this.sortType = st.asUCString;
15889                 break;
15890             case "date":
15891                 this.sortType = st.asDate;
15892                 break;
15893             default:
15894                 this.sortType = st.none;
15895         }
15896     }
15897
15898     // define once
15899     var stripRe = /[\$,%]/g;
15900
15901     // prebuilt conversion function for this field, instead of
15902     // switching every time we're reading a value
15903     if(!this.convert){
15904         var cv, dateFormat = this.dateFormat;
15905         switch(this.type){
15906             case "":
15907             case "auto":
15908             case undefined:
15909                 cv = function(v){ return v; };
15910                 break;
15911             case "string":
15912                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15913                 break;
15914             case "int":
15915                 cv = function(v){
15916                     return v !== undefined && v !== null && v !== '' ?
15917                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15918                     };
15919                 break;
15920             case "float":
15921                 cv = function(v){
15922                     return v !== undefined && v !== null && v !== '' ?
15923                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15924                     };
15925                 break;
15926             case "bool":
15927             case "boolean":
15928                 cv = function(v){ return v === true || v === "true" || v == 1; };
15929                 break;
15930             case "date":
15931                 cv = function(v){
15932                     if(!v){
15933                         return '';
15934                     }
15935                     if(v instanceof Date){
15936                         return v;
15937                     }
15938                     if(dateFormat){
15939                         if(dateFormat == "timestamp"){
15940                             return new Date(v*1000);
15941                         }
15942                         return Date.parseDate(v, dateFormat);
15943                     }
15944                     var parsed = Date.parse(v);
15945                     return parsed ? new Date(parsed) : null;
15946                 };
15947              break;
15948             
15949         }
15950         this.convert = cv;
15951     }
15952 };
15953
15954 Roo.data.Field.prototype = {
15955     dateFormat: null,
15956     defaultValue: "",
15957     mapping: null,
15958     sortType : null,
15959     sortDir : "ASC"
15960 };/*
15961  * Based on:
15962  * Ext JS Library 1.1.1
15963  * Copyright(c) 2006-2007, Ext JS, LLC.
15964  *
15965  * Originally Released Under LGPL - original licence link has changed is not relivant.
15966  *
15967  * Fork - LGPL
15968  * <script type="text/javascript">
15969  */
15970  
15971 // Base class for reading structured data from a data source.  This class is intended to be
15972 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15973
15974 /**
15975  * @class Roo.data.DataReader
15976  * @abstract
15977  * Base class for reading structured data from a data source.  This class is intended to be
15978  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15979  */
15980
15981 Roo.data.DataReader = function(meta, recordType){
15982     
15983     this.meta = meta;
15984     
15985     this.recordType = recordType instanceof Array ? 
15986         Roo.data.Record.create(recordType) : recordType;
15987 };
15988
15989 Roo.data.DataReader.prototype = {
15990     
15991     
15992     readerType : 'Data',
15993      /**
15994      * Create an empty record
15995      * @param {Object} data (optional) - overlay some values
15996      * @return {Roo.data.Record} record created.
15997      */
15998     newRow :  function(d) {
15999         var da =  {};
16000         this.recordType.prototype.fields.each(function(c) {
16001             switch( c.type) {
16002                 case 'int' : da[c.name] = 0; break;
16003                 case 'date' : da[c.name] = new Date(); break;
16004                 case 'float' : da[c.name] = 0.0; break;
16005                 case 'boolean' : da[c.name] = false; break;
16006                 default : da[c.name] = ""; break;
16007             }
16008             
16009         });
16010         return new this.recordType(Roo.apply(da, d));
16011     }
16012     
16013     
16014 };/*
16015  * Based on:
16016  * Ext JS Library 1.1.1
16017  * Copyright(c) 2006-2007, Ext JS, LLC.
16018  *
16019  * Originally Released Under LGPL - original licence link has changed is not relivant.
16020  *
16021  * Fork - LGPL
16022  * <script type="text/javascript">
16023  */
16024
16025 /**
16026  * @class Roo.data.DataProxy
16027  * @extends Roo.util.Observable
16028  * @abstract
16029  * This class is an abstract base class for implementations which provide retrieval of
16030  * unformatted data objects.<br>
16031  * <p>
16032  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16033  * (of the appropriate type which knows how to parse the data object) to provide a block of
16034  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16035  * <p>
16036  * Custom implementations must implement the load method as described in
16037  * {@link Roo.data.HttpProxy#load}.
16038  */
16039 Roo.data.DataProxy = function(){
16040     this.addEvents({
16041         /**
16042          * @event beforeload
16043          * Fires before a network request is made to retrieve a data object.
16044          * @param {Object} This DataProxy object.
16045          * @param {Object} params The params parameter to the load function.
16046          */
16047         beforeload : true,
16048         /**
16049          * @event load
16050          * Fires before the load method's callback is called.
16051          * @param {Object} This DataProxy object.
16052          * @param {Object} o The data object.
16053          * @param {Object} arg The callback argument object passed to the load function.
16054          */
16055         load : true,
16056         /**
16057          * @event loadexception
16058          * Fires if an Exception occurs during data retrieval.
16059          * @param {Object} This DataProxy object.
16060          * @param {Object} o The data object.
16061          * @param {Object} arg The callback argument object passed to the load function.
16062          * @param {Object} e The Exception.
16063          */
16064         loadexception : true
16065     });
16066     Roo.data.DataProxy.superclass.constructor.call(this);
16067 };
16068
16069 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16070
16071     /**
16072      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16073      */
16074 /*
16075  * Based on:
16076  * Ext JS Library 1.1.1
16077  * Copyright(c) 2006-2007, Ext JS, LLC.
16078  *
16079  * Originally Released Under LGPL - original licence link has changed is not relivant.
16080  *
16081  * Fork - LGPL
16082  * <script type="text/javascript">
16083  */
16084 /**
16085  * @class Roo.data.MemoryProxy
16086  * @extends Roo.data.DataProxy
16087  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16088  * to the Reader when its load method is called.
16089  * @constructor
16090  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16091  */
16092 Roo.data.MemoryProxy = function(config){
16093     var data = config;
16094     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16095         data = config.data;
16096     }
16097     Roo.data.MemoryProxy.superclass.constructor.call(this);
16098     this.data = data;
16099 };
16100
16101 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16102     
16103     /**
16104      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16105      */
16106     /**
16107      * Load data from the requested source (in this case an in-memory
16108      * data object passed to the constructor), read the data object into
16109      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16110      * process that block using the passed callback.
16111      * @param {Object} params This parameter is not used by the MemoryProxy class.
16112      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16113      * object into a block of Roo.data.Records.
16114      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16115      * The function must be passed <ul>
16116      * <li>The Record block object</li>
16117      * <li>The "arg" argument from the load function</li>
16118      * <li>A boolean success indicator</li>
16119      * </ul>
16120      * @param {Object} scope The scope in which to call the callback
16121      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16122      */
16123     load : function(params, reader, callback, scope, arg){
16124         params = params || {};
16125         var result;
16126         try {
16127             result = reader.readRecords(params.data ? params.data :this.data);
16128         }catch(e){
16129             this.fireEvent("loadexception", this, arg, null, e);
16130             callback.call(scope, null, arg, false);
16131             return;
16132         }
16133         callback.call(scope, result, arg, true);
16134     },
16135     
16136     // private
16137     update : function(params, records){
16138         
16139     }
16140 });/*
16141  * Based on:
16142  * Ext JS Library 1.1.1
16143  * Copyright(c) 2006-2007, Ext JS, LLC.
16144  *
16145  * Originally Released Under LGPL - original licence link has changed is not relivant.
16146  *
16147  * Fork - LGPL
16148  * <script type="text/javascript">
16149  */
16150 /**
16151  * @class Roo.data.HttpProxy
16152  * @extends Roo.data.DataProxy
16153  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16154  * configured to reference a certain URL.<br><br>
16155  * <p>
16156  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16157  * from which the running page was served.<br><br>
16158  * <p>
16159  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16160  * <p>
16161  * Be aware that to enable the browser to parse an XML document, the server must set
16162  * the Content-Type header in the HTTP response to "text/xml".
16163  * @constructor
16164  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16165  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16166  * will be used to make the request.
16167  */
16168 Roo.data.HttpProxy = function(conn){
16169     Roo.data.HttpProxy.superclass.constructor.call(this);
16170     // is conn a conn config or a real conn?
16171     this.conn = conn;
16172     this.useAjax = !conn || !conn.events;
16173   
16174 };
16175
16176 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16177     // thse are take from connection...
16178     
16179     /**
16180      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16181      */
16182     /**
16183      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16184      * extra parameters to each request made by this object. (defaults to undefined)
16185      */
16186     /**
16187      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16188      *  to each request made by this object. (defaults to undefined)
16189      */
16190     /**
16191      * @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)
16192      */
16193     /**
16194      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16195      */
16196      /**
16197      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16198      * @type Boolean
16199      */
16200   
16201
16202     /**
16203      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16204      * @type Boolean
16205      */
16206     /**
16207      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16208      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16209      * a finer-grained basis than the DataProxy events.
16210      */
16211     getConnection : function(){
16212         return this.useAjax ? Roo.Ajax : this.conn;
16213     },
16214
16215     /**
16216      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16217      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16218      * process that block using the passed callback.
16219      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16220      * for the request to the remote server.
16221      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16222      * object into a block of Roo.data.Records.
16223      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16224      * The function must be passed <ul>
16225      * <li>The Record block object</li>
16226      * <li>The "arg" argument from the load function</li>
16227      * <li>A boolean success indicator</li>
16228      * </ul>
16229      * @param {Object} scope The scope in which to call the callback
16230      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16231      */
16232     load : function(params, reader, callback, scope, arg){
16233         if(this.fireEvent("beforeload", this, params) !== false){
16234             var  o = {
16235                 params : params || {},
16236                 request: {
16237                     callback : callback,
16238                     scope : scope,
16239                     arg : arg
16240                 },
16241                 reader: reader,
16242                 callback : this.loadResponse,
16243                 scope: this
16244             };
16245             if(this.useAjax){
16246                 Roo.applyIf(o, this.conn);
16247                 if(this.activeRequest){
16248                     Roo.Ajax.abort(this.activeRequest);
16249                 }
16250                 this.activeRequest = Roo.Ajax.request(o);
16251             }else{
16252                 this.conn.request(o);
16253             }
16254         }else{
16255             callback.call(scope||this, null, arg, false);
16256         }
16257     },
16258
16259     // private
16260     loadResponse : function(o, success, response){
16261         delete this.activeRequest;
16262         if(!success){
16263             this.fireEvent("loadexception", this, o, response);
16264             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16265             return;
16266         }
16267         var result;
16268         try {
16269             result = o.reader.read(response);
16270         }catch(e){
16271             o.success = false;
16272             o.raw = { errorMsg : response.responseText };
16273             this.fireEvent("loadexception", this, o, response, e);
16274             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16275             return;
16276         }
16277         
16278         this.fireEvent("load", this, o, o.request.arg);
16279         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16280     },
16281
16282     // private
16283     update : function(dataSet){
16284
16285     },
16286
16287     // private
16288     updateResponse : function(dataSet){
16289
16290     }
16291 });/*
16292  * Based on:
16293  * Ext JS Library 1.1.1
16294  * Copyright(c) 2006-2007, Ext JS, LLC.
16295  *
16296  * Originally Released Under LGPL - original licence link has changed is not relivant.
16297  *
16298  * Fork - LGPL
16299  * <script type="text/javascript">
16300  */
16301
16302 /**
16303  * @class Roo.data.ScriptTagProxy
16304  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16305  * other than the originating domain of the running page.<br><br>
16306  * <p>
16307  * <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
16308  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16309  * <p>
16310  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16311  * source code that is used as the source inside a &lt;script> tag.<br><br>
16312  * <p>
16313  * In order for the browser to process the returned data, the server must wrap the data object
16314  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16315  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16316  * depending on whether the callback name was passed:
16317  * <p>
16318  * <pre><code>
16319 boolean scriptTag = false;
16320 String cb = request.getParameter("callback");
16321 if (cb != null) {
16322     scriptTag = true;
16323     response.setContentType("text/javascript");
16324 } else {
16325     response.setContentType("application/x-json");
16326 }
16327 Writer out = response.getWriter();
16328 if (scriptTag) {
16329     out.write(cb + "(");
16330 }
16331 out.print(dataBlock.toJsonString());
16332 if (scriptTag) {
16333     out.write(");");
16334 }
16335 </pre></code>
16336  *
16337  * @constructor
16338  * @param {Object} config A configuration object.
16339  */
16340 Roo.data.ScriptTagProxy = function(config){
16341     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16342     Roo.apply(this, config);
16343     this.head = document.getElementsByTagName("head")[0];
16344 };
16345
16346 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16347
16348 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16349     /**
16350      * @cfg {String} url The URL from which to request the data object.
16351      */
16352     /**
16353      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16354      */
16355     timeout : 30000,
16356     /**
16357      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16358      * the server the name of the callback function set up by the load call to process the returned data object.
16359      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16360      * javascript output which calls this named function passing the data object as its only parameter.
16361      */
16362     callbackParam : "callback",
16363     /**
16364      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16365      * name to the request.
16366      */
16367     nocache : true,
16368
16369     /**
16370      * Load data from the configured URL, read the data object into
16371      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16372      * process that block using the passed callback.
16373      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16374      * for the request to the remote server.
16375      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16376      * object into a block of Roo.data.Records.
16377      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16378      * The function must be passed <ul>
16379      * <li>The Record block object</li>
16380      * <li>The "arg" argument from the load function</li>
16381      * <li>A boolean success indicator</li>
16382      * </ul>
16383      * @param {Object} scope The scope in which to call the callback
16384      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16385      */
16386     load : function(params, reader, callback, scope, arg){
16387         if(this.fireEvent("beforeload", this, params) !== false){
16388
16389             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16390
16391             var url = this.url;
16392             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16393             if(this.nocache){
16394                 url += "&_dc=" + (new Date().getTime());
16395             }
16396             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16397             var trans = {
16398                 id : transId,
16399                 cb : "stcCallback"+transId,
16400                 scriptId : "stcScript"+transId,
16401                 params : params,
16402                 arg : arg,
16403                 url : url,
16404                 callback : callback,
16405                 scope : scope,
16406                 reader : reader
16407             };
16408             var conn = this;
16409
16410             window[trans.cb] = function(o){
16411                 conn.handleResponse(o, trans);
16412             };
16413
16414             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16415
16416             if(this.autoAbort !== false){
16417                 this.abort();
16418             }
16419
16420             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16421
16422             var script = document.createElement("script");
16423             script.setAttribute("src", url);
16424             script.setAttribute("type", "text/javascript");
16425             script.setAttribute("id", trans.scriptId);
16426             this.head.appendChild(script);
16427
16428             this.trans = trans;
16429         }else{
16430             callback.call(scope||this, null, arg, false);
16431         }
16432     },
16433
16434     // private
16435     isLoading : function(){
16436         return this.trans ? true : false;
16437     },
16438
16439     /**
16440      * Abort the current server request.
16441      */
16442     abort : function(){
16443         if(this.isLoading()){
16444             this.destroyTrans(this.trans);
16445         }
16446     },
16447
16448     // private
16449     destroyTrans : function(trans, isLoaded){
16450         this.head.removeChild(document.getElementById(trans.scriptId));
16451         clearTimeout(trans.timeoutId);
16452         if(isLoaded){
16453             window[trans.cb] = undefined;
16454             try{
16455                 delete window[trans.cb];
16456             }catch(e){}
16457         }else{
16458             // if hasn't been loaded, wait for load to remove it to prevent script error
16459             window[trans.cb] = function(){
16460                 window[trans.cb] = undefined;
16461                 try{
16462                     delete window[trans.cb];
16463                 }catch(e){}
16464             };
16465         }
16466     },
16467
16468     // private
16469     handleResponse : function(o, trans){
16470         this.trans = false;
16471         this.destroyTrans(trans, true);
16472         var result;
16473         try {
16474             result = trans.reader.readRecords(o);
16475         }catch(e){
16476             this.fireEvent("loadexception", this, o, trans.arg, e);
16477             trans.callback.call(trans.scope||window, null, trans.arg, false);
16478             return;
16479         }
16480         this.fireEvent("load", this, o, trans.arg);
16481         trans.callback.call(trans.scope||window, result, trans.arg, true);
16482     },
16483
16484     // private
16485     handleFailure : function(trans){
16486         this.trans = false;
16487         this.destroyTrans(trans, false);
16488         this.fireEvent("loadexception", this, null, trans.arg);
16489         trans.callback.call(trans.scope||window, null, trans.arg, false);
16490     }
16491 });/*
16492  * Based on:
16493  * Ext JS Library 1.1.1
16494  * Copyright(c) 2006-2007, Ext JS, LLC.
16495  *
16496  * Originally Released Under LGPL - original licence link has changed is not relivant.
16497  *
16498  * Fork - LGPL
16499  * <script type="text/javascript">
16500  */
16501
16502 /**
16503  * @class Roo.data.JsonReader
16504  * @extends Roo.data.DataReader
16505  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16506  * based on mappings in a provided Roo.data.Record constructor.
16507  * 
16508  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16509  * in the reply previously. 
16510  * 
16511  * <p>
16512  * Example code:
16513  * <pre><code>
16514 var RecordDef = Roo.data.Record.create([
16515     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16516     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16517 ]);
16518 var myReader = new Roo.data.JsonReader({
16519     totalProperty: "results",    // The property which contains the total dataset size (optional)
16520     root: "rows",                // The property which contains an Array of row objects
16521     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16522 }, RecordDef);
16523 </code></pre>
16524  * <p>
16525  * This would consume a JSON file like this:
16526  * <pre><code>
16527 { 'results': 2, 'rows': [
16528     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16529     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16530 }
16531 </code></pre>
16532  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16533  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16534  * paged from the remote server.
16535  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16536  * @cfg {String} root name of the property which contains the Array of row objects.
16537  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16538  * @cfg {Array} fields Array of field definition objects
16539  * @constructor
16540  * Create a new JsonReader
16541  * @param {Object} meta Metadata configuration options
16542  * @param {Object} recordType Either an Array of field definition objects,
16543  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16544  */
16545 Roo.data.JsonReader = function(meta, recordType){
16546     
16547     meta = meta || {};
16548     // set some defaults:
16549     Roo.applyIf(meta, {
16550         totalProperty: 'total',
16551         successProperty : 'success',
16552         root : 'data',
16553         id : 'id'
16554     });
16555     
16556     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16557 };
16558 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16559     
16560     readerType : 'Json',
16561     
16562     /**
16563      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16564      * Used by Store query builder to append _requestMeta to params.
16565      * 
16566      */
16567     metaFromRemote : false,
16568     /**
16569      * This method is only used by a DataProxy which has retrieved data from a remote server.
16570      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16571      * @return {Object} data A data block which is used by an Roo.data.Store object as
16572      * a cache of Roo.data.Records.
16573      */
16574     read : function(response){
16575         var json = response.responseText;
16576        
16577         var o = /* eval:var:o */ eval("("+json+")");
16578         if(!o) {
16579             throw {message: "JsonReader.read: Json object not found"};
16580         }
16581         
16582         if(o.metaData){
16583             
16584             delete this.ef;
16585             this.metaFromRemote = true;
16586             this.meta = o.metaData;
16587             this.recordType = Roo.data.Record.create(o.metaData.fields);
16588             this.onMetaChange(this.meta, this.recordType, o);
16589         }
16590         return this.readRecords(o);
16591     },
16592
16593     // private function a store will implement
16594     onMetaChange : function(meta, recordType, o){
16595
16596     },
16597
16598     /**
16599          * @ignore
16600          */
16601     simpleAccess: function(obj, subsc) {
16602         return obj[subsc];
16603     },
16604
16605         /**
16606          * @ignore
16607          */
16608     getJsonAccessor: function(){
16609         var re = /[\[\.]/;
16610         return function(expr) {
16611             try {
16612                 return(re.test(expr))
16613                     ? new Function("obj", "return obj." + expr)
16614                     : function(obj){
16615                         return obj[expr];
16616                     };
16617             } catch(e){}
16618             return Roo.emptyFn;
16619         };
16620     }(),
16621
16622     /**
16623      * Create a data block containing Roo.data.Records from an XML document.
16624      * @param {Object} o An object which contains an Array of row objects in the property specified
16625      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16626      * which contains the total size of the dataset.
16627      * @return {Object} data A data block which is used by an Roo.data.Store object as
16628      * a cache of Roo.data.Records.
16629      */
16630     readRecords : function(o){
16631         /**
16632          * After any data loads, the raw JSON data is available for further custom processing.
16633          * @type Object
16634          */
16635         this.o = o;
16636         var s = this.meta, Record = this.recordType,
16637             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16638
16639 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16640         if (!this.ef) {
16641             if(s.totalProperty) {
16642                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16643                 }
16644                 if(s.successProperty) {
16645                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16646                 }
16647                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16648                 if (s.id) {
16649                         var g = this.getJsonAccessor(s.id);
16650                         this.getId = function(rec) {
16651                                 var r = g(rec);  
16652                                 return (r === undefined || r === "") ? null : r;
16653                         };
16654                 } else {
16655                         this.getId = function(){return null;};
16656                 }
16657             this.ef = [];
16658             for(var jj = 0; jj < fl; jj++){
16659                 f = fi[jj];
16660                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16661                 this.ef[jj] = this.getJsonAccessor(map);
16662             }
16663         }
16664
16665         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16666         if(s.totalProperty){
16667             var vt = parseInt(this.getTotal(o), 10);
16668             if(!isNaN(vt)){
16669                 totalRecords = vt;
16670             }
16671         }
16672         if(s.successProperty){
16673             var vs = this.getSuccess(o);
16674             if(vs === false || vs === 'false'){
16675                 success = false;
16676             }
16677         }
16678         var records = [];
16679         for(var i = 0; i < c; i++){
16680             var n = root[i];
16681             var values = {};
16682             var id = this.getId(n);
16683             for(var j = 0; j < fl; j++){
16684                 f = fi[j];
16685                                 var v = this.ef[j](n);
16686                                 if (!f.convert) {
16687                                         Roo.log('missing convert for ' + f.name);
16688                                         Roo.log(f);
16689                                         continue;
16690                                 }
16691                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16692             }
16693                         if (!Record) {
16694                                 return {
16695                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16696                                         success : false,
16697                                         records : [],
16698                                         totalRecords : 0
16699                                 };
16700                         }
16701             var record = new Record(values, id);
16702             record.json = n;
16703             records[i] = record;
16704         }
16705         return {
16706             raw : o,
16707             success : success,
16708             records : records,
16709             totalRecords : totalRecords
16710         };
16711     },
16712     // used when loading children.. @see loadDataFromChildren
16713     toLoadData: function(rec)
16714     {
16715         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16716         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16717         return { data : data, total : data.length };
16718         
16719     }
16720 });/*
16721  * Based on:
16722  * Ext JS Library 1.1.1
16723  * Copyright(c) 2006-2007, Ext JS, LLC.
16724  *
16725  * Originally Released Under LGPL - original licence link has changed is not relivant.
16726  *
16727  * Fork - LGPL
16728  * <script type="text/javascript">
16729  */
16730
16731 /**
16732  * @class Roo.data.ArrayReader
16733  * @extends Roo.data.DataReader
16734  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16735  * Each element of that Array represents a row of data fields. The
16736  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16737  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16738  * <p>
16739  * Example code:.
16740  * <pre><code>
16741 var RecordDef = Roo.data.Record.create([
16742     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16743     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16744 ]);
16745 var myReader = new Roo.data.ArrayReader({
16746     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16747 }, RecordDef);
16748 </code></pre>
16749  * <p>
16750  * This would consume an Array like this:
16751  * <pre><code>
16752 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16753   </code></pre>
16754  
16755  * @constructor
16756  * Create a new JsonReader
16757  * @param {Object} meta Metadata configuration options.
16758  * @param {Object|Array} recordType Either an Array of field definition objects
16759  * 
16760  * @cfg {Array} fields Array of field definition objects
16761  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16762  * as specified to {@link Roo.data.Record#create},
16763  * or an {@link Roo.data.Record} object
16764  *
16765  * 
16766  * created using {@link Roo.data.Record#create}.
16767  */
16768 Roo.data.ArrayReader = function(meta, recordType)
16769 {    
16770     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16771 };
16772
16773 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16774     
16775       /**
16776      * Create a data block containing Roo.data.Records from an XML document.
16777      * @param {Object} o An Array of row objects which represents the dataset.
16778      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16779      * a cache of Roo.data.Records.
16780      */
16781     readRecords : function(o)
16782     {
16783         var sid = this.meta ? this.meta.id : null;
16784         var recordType = this.recordType, fields = recordType.prototype.fields;
16785         var records = [];
16786         var root = o;
16787         for(var i = 0; i < root.length; i++){
16788             var n = root[i];
16789             var values = {};
16790             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16791             for(var j = 0, jlen = fields.length; j < jlen; j++){
16792                 var f = fields.items[j];
16793                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16794                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16795                 v = f.convert(v);
16796                 values[f.name] = v;
16797             }
16798             var record = new recordType(values, id);
16799             record.json = n;
16800             records[records.length] = record;
16801         }
16802         return {
16803             records : records,
16804             totalRecords : records.length
16805         };
16806     },
16807     // used when loading children.. @see loadDataFromChildren
16808     toLoadData: function(rec)
16809     {
16810         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16811         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16812         
16813     }
16814     
16815     
16816 });/*
16817  * - LGPL
16818  * * 
16819  */
16820
16821 /**
16822  * @class Roo.bootstrap.form.ComboBox
16823  * @extends Roo.bootstrap.form.TriggerField
16824  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16825  * @cfg {Boolean} append (true|false) default false
16826  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16827  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16828  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16829  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16830  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16831  * @cfg {Boolean} animate default true
16832  * @cfg {Boolean} emptyResultText only for touch device
16833  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16834  * @cfg {String} emptyTitle default ''
16835  * @cfg {Number} width fixed with? experimental
16836  * @constructor
16837  * Create a new ComboBox.
16838  * @param {Object} config Configuration options
16839  */
16840 Roo.bootstrap.form.ComboBox = function(config){
16841     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16842     this.addEvents({
16843         /**
16844          * @event expand
16845          * Fires when the dropdown list is expanded
16846         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16847         */
16848         'expand' : true,
16849         /**
16850          * @event collapse
16851          * Fires when the dropdown list is collapsed
16852         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16853         */
16854         'collapse' : true,
16855         /**
16856          * @event beforeselect
16857          * Fires before a list item is selected. Return false to cancel the selection.
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         * @param {Roo.data.Record} record The data record returned from the underlying store
16860         * @param {Number} index The index of the selected item in the dropdown list
16861         */
16862         'beforeselect' : true,
16863         /**
16864          * @event select
16865          * Fires when a list item is selected
16866         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16868         * @param {Number} index The index of the selected item in the dropdown list
16869         */
16870         'select' : true,
16871         /**
16872          * @event beforequery
16873          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16874          * The event object passed has these properties:
16875         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876         * @param {String} query The query
16877         * @param {Boolean} forceAll true to force "all" query
16878         * @param {Boolean} cancel true to cancel the query
16879         * @param {Object} e The query event object
16880         */
16881         'beforequery': true,
16882          /**
16883          * @event add
16884          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         */
16887         'add' : true,
16888         /**
16889          * @event edit
16890          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16891         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16893         */
16894         'edit' : true,
16895         /**
16896          * @event remove
16897          * Fires when the remove value from the combobox array
16898         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16899         */
16900         'remove' : true,
16901         /**
16902          * @event afterremove
16903          * Fires when the remove value from the combobox array
16904         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16905         */
16906         'afterremove' : true,
16907         /**
16908          * @event specialfilter
16909          * Fires when specialfilter
16910             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16911             */
16912         'specialfilter' : true,
16913         /**
16914          * @event tick
16915          * Fires when tick the element
16916             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16917             */
16918         'tick' : true,
16919         /**
16920          * @event touchviewdisplay
16921          * Fires when touch view require special display (default is using displayField)
16922             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16923             * @param {Object} cfg set html .
16924             */
16925         'touchviewdisplay' : true
16926         
16927     });
16928     
16929     this.item = [];
16930     this.tickItems = [];
16931     
16932     this.selectedIndex = -1;
16933     if(this.mode == 'local'){
16934         if(config.queryDelay === undefined){
16935             this.queryDelay = 10;
16936         }
16937         if(config.minChars === undefined){
16938             this.minChars = 0;
16939         }
16940     }
16941 };
16942
16943 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16944      
16945     /**
16946      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16947      * rendering into an Roo.Editor, defaults to false)
16948      */
16949     /**
16950      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16951      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16952      */
16953     /**
16954      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16955      */
16956     /**
16957      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16958      * the dropdown list (defaults to undefined, with no header element)
16959      */
16960
16961      /**
16962      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16963      */
16964      
16965      /**
16966      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16967      */
16968     listWidth: undefined,
16969     /**
16970      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16971      * mode = 'remote' or 'text' if mode = 'local')
16972      */
16973     displayField: undefined,
16974     
16975     /**
16976      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16977      * mode = 'remote' or 'value' if mode = 'local'). 
16978      * Note: use of a valueField requires the user make a selection
16979      * in order for a value to be mapped.
16980      */
16981     valueField: undefined,
16982     /**
16983      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16984      */
16985     modalTitle : '',
16986     
16987     /**
16988      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16989      * field's data value (defaults to the underlying DOM element's name)
16990      */
16991     hiddenName: undefined,
16992     /**
16993      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16994      */
16995     listClass: '',
16996     /**
16997      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16998      */
16999     selectedClass: 'active',
17000     
17001     /**
17002      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17003      */
17004     shadow:'sides',
17005     /**
17006      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17007      * anchor positions (defaults to 'tl-bl')
17008      */
17009     listAlign: 'tl-bl?',
17010     /**
17011      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17012      */
17013     maxHeight: 300,
17014     /**
17015      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17016      * query specified by the allQuery config option (defaults to 'query')
17017      */
17018     triggerAction: 'query',
17019     /**
17020      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17021      * (defaults to 4, does not apply if editable = false)
17022      */
17023     minChars : 4,
17024     /**
17025      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17026      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17027      */
17028     typeAhead: false,
17029     /**
17030      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17031      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17032      */
17033     queryDelay: 500,
17034     /**
17035      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17036      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17037      */
17038     pageSize: 0,
17039     /**
17040      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17041      * when editable = true (defaults to false)
17042      */
17043     selectOnFocus:false,
17044     /**
17045      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17046      */
17047     queryParam: 'query',
17048     /**
17049      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17050      * when mode = 'remote' (defaults to 'Loading...')
17051      */
17052     loadingText: 'Loading...',
17053     /**
17054      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17055      */
17056     resizable: false,
17057     /**
17058      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17059      */
17060     handleHeight : 8,
17061     /**
17062      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17063      * traditional select (defaults to true)
17064      */
17065     editable: true,
17066     /**
17067      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17068      */
17069     allQuery: '',
17070     /**
17071      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17072      */
17073     mode: 'remote',
17074     /**
17075      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17076      * listWidth has a higher value)
17077      */
17078     minListWidth : 70,
17079     /**
17080      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17081      * allow the user to set arbitrary text into the field (defaults to false)
17082      */
17083     forceSelection:false,
17084     /**
17085      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17086      * if typeAhead = true (defaults to 250)
17087      */
17088     typeAheadDelay : 250,
17089     /**
17090      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17091      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17092      */
17093     valueNotFoundText : undefined,
17094     /**
17095      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17096      */
17097     blockFocus : false,
17098     
17099     /**
17100      * @cfg {Boolean} disableClear Disable showing of clear button.
17101      */
17102     disableClear : false,
17103     /**
17104      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17105      */
17106     alwaysQuery : false,
17107     
17108     /**
17109      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17110      */
17111     multiple : false,
17112     
17113     /**
17114      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17115      */
17116     invalidClass : "has-warning",
17117     
17118     /**
17119      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17120      */
17121     validClass : "has-success",
17122     
17123     /**
17124      * @cfg {Boolean} specialFilter (true|false) special filter default false
17125      */
17126     specialFilter : false,
17127     
17128     /**
17129      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17130      */
17131     mobileTouchView : true,
17132     
17133     /**
17134      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17135      */
17136     useNativeIOS : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17140      */
17141     mobile_restrict_height : false,
17142     
17143     ios_options : false,
17144     
17145     //private
17146     addicon : false,
17147     editicon: false,
17148     
17149     page: 0,
17150     hasQuery: false,
17151     append: false,
17152     loadNext: false,
17153     autoFocus : true,
17154     tickable : false,
17155     btnPosition : 'right',
17156     triggerList : true,
17157     showToggleBtn : true,
17158     animate : true,
17159     emptyResultText: 'Empty',
17160     triggerText : 'Select',
17161     emptyTitle : '',
17162     width : false,
17163     
17164     // element that contains real text value.. (when hidden is used..)
17165     
17166     getAutoCreate : function()
17167     {   
17168         var cfg = false;
17169         //render
17170         /*
17171          * Render classic select for iso
17172          */
17173         
17174         if(Roo.isIOS && this.useNativeIOS){
17175             cfg = this.getAutoCreateNativeIOS();
17176             return cfg;
17177         }
17178         
17179         /*
17180          * Touch Devices
17181          */
17182         
17183         if(Roo.isTouch && this.mobileTouchView){
17184             cfg = this.getAutoCreateTouchView();
17185             return cfg;;
17186         }
17187         
17188         /*
17189          *  Normal ComboBox
17190          */
17191         if(!this.tickable){
17192             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17193             return cfg;
17194         }
17195         
17196         /*
17197          *  ComboBox with tickable selections
17198          */
17199              
17200         var align = this.labelAlign || this.parentLabelAlign();
17201         
17202         cfg = {
17203             cls : 'form-group roo-combobox-tickable' //input-group
17204         };
17205         
17206         var btn_text_select = '';
17207         var btn_text_done = '';
17208         var btn_text_cancel = '';
17209         
17210         if (this.btn_text_show) {
17211             btn_text_select = 'Select';
17212             btn_text_done = 'Done';
17213             btn_text_cancel = 'Cancel'; 
17214         }
17215         
17216         var buttons = {
17217             tag : 'div',
17218             cls : 'tickable-buttons',
17219             cn : [
17220                 {
17221                     tag : 'button',
17222                     type : 'button',
17223                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17224                     //html : this.triggerText
17225                     html: btn_text_select
17226                 },
17227                 {
17228                     tag : 'button',
17229                     type : 'button',
17230                     name : 'ok',
17231                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17232                     //html : 'Done'
17233                     html: btn_text_done
17234                 },
17235                 {
17236                     tag : 'button',
17237                     type : 'button',
17238                     name : 'cancel',
17239                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17240                     //html : 'Cancel'
17241                     html: btn_text_cancel
17242                 }
17243             ]
17244         };
17245         
17246         if(this.editable){
17247             buttons.cn.unshift({
17248                 tag: 'input',
17249                 cls: 'roo-select2-search-field-input'
17250             });
17251         }
17252         
17253         var _this = this;
17254         
17255         Roo.each(buttons.cn, function(c){
17256             if (_this.size) {
17257                 c.cls += ' btn-' + _this.size;
17258             }
17259
17260             if (_this.disabled) {
17261                 c.disabled = true;
17262             }
17263         });
17264         
17265         var box = {
17266             tag: 'div',
17267             style : 'display: contents',
17268             cn: [
17269                 {
17270                     tag: 'input',
17271                     type : 'hidden',
17272                     cls: 'form-hidden-field'
17273                 },
17274                 {
17275                     tag: 'ul',
17276                     cls: 'roo-select2-choices',
17277                     cn:[
17278                         {
17279                             tag: 'li',
17280                             cls: 'roo-select2-search-field',
17281                             cn: [
17282                                 buttons
17283                             ]
17284                         }
17285                     ]
17286                 }
17287             ]
17288         };
17289         
17290         var combobox = {
17291             cls: 'roo-select2-container input-group roo-select2-container-multi',
17292             cn: [
17293                 
17294                 box
17295 //                {
17296 //                    tag: 'ul',
17297 //                    cls: 'typeahead typeahead-long dropdown-menu',
17298 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17299 //                }
17300             ]
17301         };
17302         
17303         if(this.hasFeedback && !this.allowBlank){
17304             
17305             var feedback = {
17306                 tag: 'span',
17307                 cls: 'glyphicon form-control-feedback'
17308             };
17309
17310             combobox.cn.push(feedback);
17311         }
17312         
17313         
17314         
17315         var indicator = {
17316             tag : 'i',
17317             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17318             tooltip : 'This field is required'
17319         };
17320         if (Roo.bootstrap.version == 4) {
17321             indicator = {
17322                 tag : 'i',
17323                 style : 'display:none'
17324             };
17325         }
17326         if (align ==='left' && this.fieldLabel.length) {
17327             
17328             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17329             
17330             cfg.cn = [
17331                 indicator,
17332                 {
17333                     tag: 'label',
17334                     'for' :  id,
17335                     cls : 'control-label col-form-label',
17336                     html : this.fieldLabel
17337
17338                 },
17339                 {
17340                     cls : "", 
17341                     cn: [
17342                         combobox
17343                     ]
17344                 }
17345
17346             ];
17347             
17348             var labelCfg = cfg.cn[1];
17349             var contentCfg = cfg.cn[2];
17350             
17351
17352             if(this.indicatorpos == 'right'){
17353                 
17354                 cfg.cn = [
17355                     {
17356                         tag: 'label',
17357                         'for' :  id,
17358                         cls : 'control-label col-form-label',
17359                         cn : [
17360                             {
17361                                 tag : 'span',
17362                                 html : this.fieldLabel
17363                             },
17364                             indicator
17365                         ]
17366                     },
17367                     {
17368                         cls : "",
17369                         cn: [
17370                             combobox
17371                         ]
17372                     }
17373
17374                 ];
17375                 
17376                 
17377                 
17378                 labelCfg = cfg.cn[0];
17379                 contentCfg = cfg.cn[1];
17380             
17381             }
17382             
17383             if(this.labelWidth > 12){
17384                 labelCfg.style = "width: " + this.labelWidth + 'px';
17385             }
17386             if(this.width * 1 > 0){
17387                 contentCfg.style = "width: " + this.width + 'px';
17388             }
17389             if(this.labelWidth < 13 && this.labelmd == 0){
17390                 this.labelmd = this.labelWidth;
17391             }
17392             
17393             if(this.labellg > 0){
17394                 labelCfg.cls += ' col-lg-' + this.labellg;
17395                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17396             }
17397             
17398             if(this.labelmd > 0){
17399                 labelCfg.cls += ' col-md-' + this.labelmd;
17400                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17401             }
17402             
17403             if(this.labelsm > 0){
17404                 labelCfg.cls += ' col-sm-' + this.labelsm;
17405                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17406             }
17407             
17408             if(this.labelxs > 0){
17409                 labelCfg.cls += ' col-xs-' + this.labelxs;
17410                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17411             }
17412                 
17413                 
17414         } else if ( this.fieldLabel.length) {
17415 //                Roo.log(" label");
17416                  cfg.cn = [
17417                    indicator,
17418                     {
17419                         tag: 'label',
17420                         //cls : 'input-group-addon',
17421                         html : this.fieldLabel
17422                     },
17423                     combobox
17424                 ];
17425                 
17426                 if(this.indicatorpos == 'right'){
17427                     cfg.cn = [
17428                         {
17429                             tag: 'label',
17430                             //cls : 'input-group-addon',
17431                             html : this.fieldLabel
17432                         },
17433                         indicator,
17434                         combobox
17435                     ];
17436                     
17437                 }
17438
17439         } else {
17440             
17441 //                Roo.log(" no label && no align");
17442                 cfg = combobox
17443                      
17444                 
17445         }
17446          
17447         var settings=this;
17448         ['xs','sm','md','lg'].map(function(size){
17449             if (settings[size]) {
17450                 cfg.cls += ' col-' + size + '-' + settings[size];
17451             }
17452         });
17453         
17454         return cfg;
17455         
17456     },
17457     
17458     _initEventsCalled : false,
17459     
17460     // private
17461     initEvents: function()
17462     {   
17463         if (this._initEventsCalled) { // as we call render... prevent looping...
17464             return;
17465         }
17466         this._initEventsCalled = true;
17467         
17468         if (!this.store) {
17469             throw "can not find store for combo";
17470         }
17471         
17472         this.indicator = this.indicatorEl();
17473         
17474         this.store = Roo.factory(this.store, Roo.data);
17475         this.store.parent = this;
17476         
17477         // if we are building from html. then this element is so complex, that we can not really
17478         // use the rendered HTML.
17479         // so we have to trash and replace the previous code.
17480         if (Roo.XComponent.build_from_html) {
17481             // remove this element....
17482             var e = this.el.dom, k=0;
17483             while (e ) { e = e.previousSibling;  ++k;}
17484
17485             this.el.remove();
17486             
17487             this.el=false;
17488             this.rendered = false;
17489             
17490             this.render(this.parent().getChildContainer(true), k);
17491         }
17492         
17493         if(Roo.isIOS && this.useNativeIOS){
17494             this.initIOSView();
17495             return;
17496         }
17497         
17498         /*
17499          * Touch Devices
17500          */
17501         
17502         if(Roo.isTouch && this.mobileTouchView){
17503             this.initTouchView();
17504             return;
17505         }
17506         
17507         if(this.tickable){
17508             this.initTickableEvents();
17509             return;
17510         }
17511         
17512         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17513         
17514         if(this.hiddenName){
17515             
17516             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17517             
17518             this.hiddenField.dom.value =
17519                 this.hiddenValue !== undefined ? this.hiddenValue :
17520                 this.value !== undefined ? this.value : '';
17521
17522             // prevent input submission
17523             this.el.dom.removeAttribute('name');
17524             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17525              
17526              
17527         }
17528         //if(Roo.isGecko){
17529         //    this.el.dom.setAttribute('autocomplete', 'off');
17530         //}
17531         
17532         var cls = 'x-combo-list';
17533         
17534         //this.list = new Roo.Layer({
17535         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17536         //});
17537         
17538         var _this = this;
17539         
17540         (function(){
17541             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17542             _this.list.setWidth(lw);
17543         }).defer(100);
17544         
17545         this.list.on('mouseover', this.onViewOver, this);
17546         this.list.on('mousemove', this.onViewMove, this);
17547         this.list.on('scroll', this.onViewScroll, this);
17548         
17549         /*
17550         this.list.swallowEvent('mousewheel');
17551         this.assetHeight = 0;
17552
17553         if(this.title){
17554             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17555             this.assetHeight += this.header.getHeight();
17556         }
17557
17558         this.innerList = this.list.createChild({cls:cls+'-inner'});
17559         this.innerList.on('mouseover', this.onViewOver, this);
17560         this.innerList.on('mousemove', this.onViewMove, this);
17561         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17562         
17563         if(this.allowBlank && !this.pageSize && !this.disableClear){
17564             this.footer = this.list.createChild({cls:cls+'-ft'});
17565             this.pageTb = new Roo.Toolbar(this.footer);
17566            
17567         }
17568         if(this.pageSize){
17569             this.footer = this.list.createChild({cls:cls+'-ft'});
17570             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17571                     {pageSize: this.pageSize});
17572             
17573         }
17574         
17575         if (this.pageTb && this.allowBlank && !this.disableClear) {
17576             var _this = this;
17577             this.pageTb.add(new Roo.Toolbar.Fill(), {
17578                 cls: 'x-btn-icon x-btn-clear',
17579                 text: '&#160;',
17580                 handler: function()
17581                 {
17582                     _this.collapse();
17583                     _this.clearValue();
17584                     _this.onSelect(false, -1);
17585                 }
17586             });
17587         }
17588         if (this.footer) {
17589             this.assetHeight += this.footer.getHeight();
17590         }
17591         */
17592             
17593         if(!this.tpl){
17594             this.tpl = Roo.bootstrap.version == 4 ?
17595                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17596                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17597         }
17598
17599         this.view = new Roo.View(this.list, this.tpl, {
17600             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17601         });
17602         //this.view.wrapEl.setDisplayed(false);
17603         this.view.on('click', this.onViewClick, this);
17604         
17605         
17606         this.store.on('beforeload', this.onBeforeLoad, this);
17607         this.store.on('load', this.onLoad, this);
17608         this.store.on('loadexception', this.onLoadException, this);
17609         /*
17610         if(this.resizable){
17611             this.resizer = new Roo.Resizable(this.list,  {
17612                pinned:true, handles:'se'
17613             });
17614             this.resizer.on('resize', function(r, w, h){
17615                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17616                 this.listWidth = w;
17617                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17618                 this.restrictHeight();
17619             }, this);
17620             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17621         }
17622         */
17623         if(!this.editable){
17624             this.editable = true;
17625             this.setEditable(false);
17626         }
17627         
17628         /*
17629         
17630         if (typeof(this.events.add.listeners) != 'undefined') {
17631             
17632             this.addicon = this.wrap.createChild(
17633                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17634        
17635             this.addicon.on('click', function(e) {
17636                 this.fireEvent('add', this);
17637             }, this);
17638         }
17639         if (typeof(this.events.edit.listeners) != 'undefined') {
17640             
17641             this.editicon = this.wrap.createChild(
17642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17643             if (this.addicon) {
17644                 this.editicon.setStyle('margin-left', '40px');
17645             }
17646             this.editicon.on('click', function(e) {
17647                 
17648                 // we fire even  if inothing is selected..
17649                 this.fireEvent('edit', this, this.lastData );
17650                 
17651             }, this);
17652         }
17653         */
17654         
17655         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17656             "up" : function(e){
17657                 this.inKeyMode = true;
17658                 this.selectPrev();
17659             },
17660
17661             "down" : function(e){
17662                 if(!this.isExpanded()){
17663                     this.onTriggerClick();
17664                 }else{
17665                     this.inKeyMode = true;
17666                     this.selectNext();
17667                 }
17668             },
17669
17670             "enter" : function(e){
17671 //                this.onViewClick();
17672                 //return true;
17673                 this.collapse();
17674                 
17675                 if(this.fireEvent("specialkey", this, e)){
17676                     this.onViewClick(false);
17677                 }
17678                 
17679                 return true;
17680             },
17681
17682             "esc" : function(e){
17683                 this.collapse();
17684             },
17685
17686             "tab" : function(e){
17687                 this.collapse();
17688                 
17689                 if(this.fireEvent("specialkey", this, e)){
17690                     this.onViewClick(false);
17691                 }
17692                 
17693                 return true;
17694             },
17695
17696             scope : this,
17697
17698             doRelay : function(foo, bar, hname){
17699                 if(hname == 'down' || this.scope.isExpanded()){
17700                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17701                 }
17702                 return true;
17703             },
17704
17705             forceKeyDown: true
17706         });
17707         
17708         
17709         this.queryDelay = Math.max(this.queryDelay || 10,
17710                 this.mode == 'local' ? 10 : 250);
17711         
17712         
17713         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17714         
17715         if(this.typeAhead){
17716             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17717         }
17718         if(this.editable !== false){
17719             this.inputEl().on("keyup", this.onKeyUp, this);
17720         }
17721         if(this.forceSelection){
17722             this.inputEl().on('blur', this.doForce, this);
17723         }
17724         
17725         if(this.multiple){
17726             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17727             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17728         }
17729     },
17730     
17731     initTickableEvents: function()
17732     {   
17733         this.createList();
17734         
17735         if(this.hiddenName){
17736             
17737             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17738             
17739             this.hiddenField.dom.value =
17740                 this.hiddenValue !== undefined ? this.hiddenValue :
17741                 this.value !== undefined ? this.value : '';
17742
17743             // prevent input submission
17744             this.el.dom.removeAttribute('name');
17745             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17746              
17747              
17748         }
17749         
17750 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17751         
17752         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17753         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17754         if(this.triggerList){
17755             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17756         }
17757          
17758         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17759         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17760         
17761         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17762         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17763         
17764         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17765         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17766         
17767         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17768         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17769         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17770         
17771         this.okBtn.hide();
17772         this.cancelBtn.hide();
17773         
17774         var _this = this;
17775         
17776         (function(){
17777             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17778             _this.list.setWidth(lw);
17779         }).defer(100);
17780         
17781         this.list.on('mouseover', this.onViewOver, this);
17782         this.list.on('mousemove', this.onViewMove, this);
17783         
17784         this.list.on('scroll', this.onViewScroll, this);
17785         
17786         if(!this.tpl){
17787             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17788                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17789         }
17790
17791         this.view = new Roo.View(this.list, this.tpl, {
17792             singleSelect:true,
17793             tickable:true,
17794             parent:this,
17795             store: this.store,
17796             selectedClass: this.selectedClass
17797         });
17798         
17799         //this.view.wrapEl.setDisplayed(false);
17800         this.view.on('click', this.onViewClick, this);
17801         
17802         
17803         
17804         this.store.on('beforeload', this.onBeforeLoad, this);
17805         this.store.on('load', this.onLoad, this);
17806         this.store.on('loadexception', this.onLoadException, this);
17807         
17808         if(this.editable){
17809             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17810                 "up" : function(e){
17811                     this.inKeyMode = true;
17812                     this.selectPrev();
17813                 },
17814
17815                 "down" : function(e){
17816                     this.inKeyMode = true;
17817                     this.selectNext();
17818                 },
17819
17820                 "enter" : function(e){
17821                     if(this.fireEvent("specialkey", this, e)){
17822                         this.onViewClick(false);
17823                     }
17824                     
17825                     return true;
17826                 },
17827
17828                 "esc" : function(e){
17829                     this.onTickableFooterButtonClick(e, false, false);
17830                 },
17831
17832                 "tab" : function(e){
17833                     this.fireEvent("specialkey", this, e);
17834                     
17835                     this.onTickableFooterButtonClick(e, false, false);
17836                     
17837                     return true;
17838                 },
17839
17840                 scope : this,
17841
17842                 doRelay : function(e, fn, key){
17843                     if(this.scope.isExpanded()){
17844                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17845                     }
17846                     return true;
17847                 },
17848
17849                 forceKeyDown: true
17850             });
17851         }
17852         
17853         this.queryDelay = Math.max(this.queryDelay || 10,
17854                 this.mode == 'local' ? 10 : 250);
17855         
17856         
17857         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17858         
17859         if(this.typeAhead){
17860             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17861         }
17862         
17863         if(this.editable !== false){
17864             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17865         }
17866         
17867         this.indicator = this.indicatorEl();
17868         
17869         if(this.indicator){
17870             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17871             this.indicator.hide();
17872         }
17873         
17874     },
17875
17876     onDestroy : function(){
17877         if(this.view){
17878             this.view.setStore(null);
17879             this.view.el.removeAllListeners();
17880             this.view.el.remove();
17881             this.view.purgeListeners();
17882         }
17883         if(this.list){
17884             this.list.dom.innerHTML  = '';
17885         }
17886         
17887         if(this.store){
17888             this.store.un('beforeload', this.onBeforeLoad, this);
17889             this.store.un('load', this.onLoad, this);
17890             this.store.un('loadexception', this.onLoadException, this);
17891         }
17892         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17893     },
17894
17895     // private
17896     fireKey : function(e){
17897         if(e.isNavKeyPress() && !this.list.isVisible()){
17898             this.fireEvent("specialkey", this, e);
17899         }
17900     },
17901
17902     // private
17903     onResize: function(w, h)
17904     {
17905         
17906         
17907 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17908 //        
17909 //        if(typeof w != 'number'){
17910 //            // we do not handle it!?!?
17911 //            return;
17912 //        }
17913 //        var tw = this.trigger.getWidth();
17914 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17915 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17916 //        var x = w - tw;
17917 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17918 //            
17919 //        //this.trigger.setStyle('left', x+'px');
17920 //        
17921 //        if(this.list && this.listWidth === undefined){
17922 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17923 //            this.list.setWidth(lw);
17924 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17925 //        }
17926         
17927     
17928         
17929     },
17930
17931     /**
17932      * Allow or prevent the user from directly editing the field text.  If false is passed,
17933      * the user will only be able to select from the items defined in the dropdown list.  This method
17934      * is the runtime equivalent of setting the 'editable' config option at config time.
17935      * @param {Boolean} value True to allow the user to directly edit the field text
17936      */
17937     setEditable : function(value){
17938         if(value == this.editable){
17939             return;
17940         }
17941         this.editable = value;
17942         if(!value){
17943             this.inputEl().dom.setAttribute('readOnly', true);
17944             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17945             this.inputEl().addClass('x-combo-noedit');
17946         }else{
17947             this.inputEl().dom.removeAttribute('readOnly');
17948             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17949             this.inputEl().removeClass('x-combo-noedit');
17950         }
17951     },
17952
17953     // private
17954     
17955     onBeforeLoad : function(combo,opts){
17956         if(!this.hasFocus){
17957             return;
17958         }
17959          if (!opts.add) {
17960             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17961          }
17962         this.restrictHeight();
17963         this.selectedIndex = -1;
17964     },
17965
17966     // private
17967     onLoad : function(){
17968         
17969         this.hasQuery = false;
17970         
17971         if(!this.hasFocus){
17972             return;
17973         }
17974         
17975         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17976             this.loading.hide();
17977         }
17978         
17979         if(this.store.getCount() > 0){
17980             
17981             this.expand();
17982             this.restrictHeight();
17983             if(this.lastQuery == this.allQuery){
17984                 if(this.editable && !this.tickable){
17985                     this.inputEl().dom.select();
17986                 }
17987                 
17988                 if(
17989                     !this.selectByValue(this.value, true) &&
17990                     this.autoFocus && 
17991                     (
17992                         !this.store.lastOptions ||
17993                         typeof(this.store.lastOptions.add) == 'undefined' || 
17994                         this.store.lastOptions.add != true
17995                     )
17996                 ){
17997                     this.select(0, true);
17998                 }
17999             }else{
18000                 if(this.autoFocus){
18001                     this.selectNext();
18002                 }
18003                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18004                     this.taTask.delay(this.typeAheadDelay);
18005                 }
18006             }
18007         }else{
18008             this.onEmptyResults();
18009         }
18010         
18011         //this.el.focus();
18012     },
18013     // private
18014     onLoadException : function()
18015     {
18016         this.hasQuery = false;
18017         
18018         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18019             this.loading.hide();
18020         }
18021         
18022         if(this.tickable && this.editable){
18023             return;
18024         }
18025         
18026         this.collapse();
18027         // only causes errors at present
18028         //Roo.log(this.store.reader.jsonData);
18029         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18030             // fixme
18031             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18032         //}
18033         
18034         
18035     },
18036     // private
18037     onTypeAhead : function(){
18038         if(this.store.getCount() > 0){
18039             var r = this.store.getAt(0);
18040             var newValue = r.data[this.displayField];
18041             var len = newValue.length;
18042             var selStart = this.getRawValue().length;
18043             
18044             if(selStart != len){
18045                 this.setRawValue(newValue);
18046                 this.selectText(selStart, newValue.length);
18047             }
18048         }
18049     },
18050
18051     // private
18052     onSelect : function(record, index){
18053         
18054         if(this.fireEvent('beforeselect', this, record, index) !== false){
18055         
18056             this.setFromData(index > -1 ? record.data : false);
18057             
18058             this.collapse();
18059             this.fireEvent('select', this, record, index);
18060         }
18061     },
18062
18063     /**
18064      * Returns the currently selected field value or empty string if no value is set.
18065      * @return {String} value The selected value
18066      */
18067     getValue : function()
18068     {
18069         if(Roo.isIOS && this.useNativeIOS){
18070             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18071         }
18072         
18073         if(this.multiple){
18074             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18075         }
18076         
18077         if(this.valueField){
18078             return typeof this.value != 'undefined' ? this.value : '';
18079         }else{
18080             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18081         }
18082     },
18083     
18084     getRawValue : function()
18085     {
18086         if(Roo.isIOS && this.useNativeIOS){
18087             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18088         }
18089         
18090         var v = this.inputEl().getValue();
18091         
18092         return v;
18093     },
18094
18095     /**
18096      * Clears any text/value currently set in the field
18097      */
18098     clearValue : function(){
18099         
18100         if(this.hiddenField){
18101             this.hiddenField.dom.value = '';
18102         }
18103         this.value = '';
18104         this.setRawValue('');
18105         this.lastSelectionText = '';
18106         this.lastData = false;
18107         
18108         var close = this.closeTriggerEl();
18109         
18110         if(close){
18111             close.hide();
18112         }
18113         
18114         this.validate();
18115         
18116     },
18117
18118     /**
18119      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18120      * will be displayed in the field.  If the value does not match the data value of an existing item,
18121      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18122      * Otherwise the field will be blank (although the value will still be set).
18123      * @param {String} value The value to match
18124      */
18125     setValue : function(v)
18126     {
18127         if(Roo.isIOS && this.useNativeIOS){
18128             this.setIOSValue(v);
18129             return;
18130         }
18131         
18132         if(this.multiple){
18133             this.syncValue();
18134             return;
18135         }
18136         
18137         var text = v;
18138         if(this.valueField){
18139             var r = this.findRecord(this.valueField, v);
18140             if(r){
18141                 text = r.data[this.displayField];
18142             }else if(this.valueNotFoundText !== undefined){
18143                 text = this.valueNotFoundText;
18144             }
18145         }
18146         this.lastSelectionText = text;
18147         if(this.hiddenField){
18148             this.hiddenField.dom.value = v;
18149         }
18150         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18151         this.value = v;
18152         
18153         var close = this.closeTriggerEl();
18154         
18155         if(close){
18156             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18157         }
18158         
18159         this.validate();
18160     },
18161     /**
18162      * @property {Object} the last set data for the element
18163      */
18164     
18165     lastData : false,
18166     /**
18167      * Sets the value of the field based on a object which is related to the record format for the store.
18168      * @param {Object} value the value to set as. or false on reset?
18169      */
18170     setFromData : function(o){
18171         
18172         if(this.multiple){
18173             this.addItem(o);
18174             return;
18175         }
18176             
18177         var dv = ''; // display value
18178         var vv = ''; // value value..
18179         this.lastData = o;
18180         if (this.displayField) {
18181             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18182         } else {
18183             // this is an error condition!!!
18184             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18185         }
18186         
18187         if(this.valueField){
18188             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18189         }
18190         
18191         var close = this.closeTriggerEl();
18192         
18193         if(close){
18194             if(dv.length || vv * 1 > 0){
18195                 close.show() ;
18196                 this.blockFocus=true;
18197             } else {
18198                 close.hide();
18199             }             
18200         }
18201         
18202         if(this.hiddenField){
18203             this.hiddenField.dom.value = vv;
18204             
18205             this.lastSelectionText = dv;
18206             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18207             this.value = vv;
18208             return;
18209         }
18210         // no hidden field.. - we store the value in 'value', but still display
18211         // display field!!!!
18212         this.lastSelectionText = dv;
18213         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18214         this.value = vv;
18215         
18216         
18217         
18218     },
18219     // private
18220     reset : function(){
18221         // overridden so that last data is reset..
18222         
18223         if(this.multiple){
18224             this.clearItem();
18225             return;
18226         }
18227         
18228         this.setValue(this.originalValue);
18229         //this.clearInvalid();
18230         this.lastData = false;
18231         if (this.view) {
18232             this.view.clearSelections();
18233         }
18234         
18235         this.validate();
18236     },
18237     // private
18238     findRecord : function(prop, value){
18239         var record;
18240         if(this.store.getCount() > 0){
18241             this.store.each(function(r){
18242                 if(r.data[prop] == value){
18243                     record = r;
18244                     return false;
18245                 }
18246                 return true;
18247             });
18248         }
18249         return record;
18250     },
18251     
18252     getName: function()
18253     {
18254         // returns hidden if it's set..
18255         if (!this.rendered) {return ''};
18256         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18257         
18258     },
18259     // private
18260     onViewMove : function(e, t){
18261         this.inKeyMode = false;
18262     },
18263
18264     // private
18265     onViewOver : function(e, t){
18266         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18267             return;
18268         }
18269         var item = this.view.findItemFromChild(t);
18270         
18271         if(item){
18272             var index = this.view.indexOf(item);
18273             this.select(index, false);
18274         }
18275     },
18276
18277     // private
18278     onViewClick : function(view, doFocus, el, e)
18279     {
18280         var index = this.view.getSelectedIndexes()[0];
18281         
18282         var r = this.store.getAt(index);
18283         
18284         if(this.tickable){
18285             
18286             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18287                 return;
18288             }
18289             
18290             var rm = false;
18291             var _this = this;
18292             
18293             Roo.each(this.tickItems, function(v,k){
18294                 
18295                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18296                     Roo.log(v);
18297                     _this.tickItems.splice(k, 1);
18298                     
18299                     if(typeof(e) == 'undefined' && view == false){
18300                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18301                     }
18302                     
18303                     rm = true;
18304                     return;
18305                 }
18306             });
18307             
18308             if(rm){
18309                 return;
18310             }
18311             
18312             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18313                 this.tickItems.push(r.data);
18314             }
18315             
18316             if(typeof(e) == 'undefined' && view == false){
18317                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18318             }
18319                     
18320             return;
18321         }
18322         
18323         if(r){
18324             this.onSelect(r, index);
18325         }
18326         if(doFocus !== false && !this.blockFocus){
18327             this.inputEl().focus();
18328         }
18329     },
18330
18331     // private
18332     restrictHeight : function(){
18333         //this.innerList.dom.style.height = '';
18334         //var inner = this.innerList.dom;
18335         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18336         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18337         //this.list.beginUpdate();
18338         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18339         this.list.alignTo(this.inputEl(), this.listAlign);
18340         this.list.alignTo(this.inputEl(), this.listAlign);
18341         //this.list.endUpdate();
18342     },
18343
18344     // private
18345     onEmptyResults : function(){
18346         
18347         if(this.tickable && this.editable){
18348             this.hasFocus = false;
18349             this.restrictHeight();
18350             return;
18351         }
18352         
18353         this.collapse();
18354     },
18355
18356     /**
18357      * Returns true if the dropdown list is expanded, else false.
18358      */
18359     isExpanded : function(){
18360         return this.list.isVisible();
18361     },
18362
18363     /**
18364      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18365      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18366      * @param {String} value The data value of the item to select
18367      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18368      * selected item if it is not currently in view (defaults to true)
18369      * @return {Boolean} True if the value matched an item in the list, else false
18370      */
18371     selectByValue : function(v, scrollIntoView){
18372         if(v !== undefined && v !== null){
18373             var r = this.findRecord(this.valueField || this.displayField, v);
18374             if(r){
18375                 this.select(this.store.indexOf(r), scrollIntoView);
18376                 return true;
18377             }
18378         }
18379         return false;
18380     },
18381
18382     /**
18383      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18384      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18385      * @param {Number} index The zero-based index of the list item to select
18386      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18387      * selected item if it is not currently in view (defaults to true)
18388      */
18389     select : function(index, scrollIntoView){
18390         this.selectedIndex = index;
18391         this.view.select(index);
18392         if(scrollIntoView !== false){
18393             var el = this.view.getNode(index);
18394             /*
18395              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18396              */
18397             if(el){
18398                 this.list.scrollChildIntoView(el, false);
18399             }
18400         }
18401     },
18402
18403     // private
18404     selectNext : function(){
18405         var ct = this.store.getCount();
18406         if(ct > 0){
18407             if(this.selectedIndex == -1){
18408                 this.select(0);
18409             }else if(this.selectedIndex < ct-1){
18410                 this.select(this.selectedIndex+1);
18411             }
18412         }
18413     },
18414
18415     // private
18416     selectPrev : function(){
18417         var ct = this.store.getCount();
18418         if(ct > 0){
18419             if(this.selectedIndex == -1){
18420                 this.select(0);
18421             }else if(this.selectedIndex != 0){
18422                 this.select(this.selectedIndex-1);
18423             }
18424         }
18425     },
18426
18427     // private
18428     onKeyUp : function(e){
18429         if(this.editable !== false && !e.isSpecialKey()){
18430             this.lastKey = e.getKey();
18431             this.dqTask.delay(this.queryDelay);
18432         }
18433     },
18434
18435     // private
18436     validateBlur : function(){
18437         return !this.list || !this.list.isVisible();   
18438     },
18439
18440     // private
18441     initQuery : function(){
18442         
18443         var v = this.getRawValue();
18444         
18445         if(this.tickable && this.editable){
18446             v = this.tickableInputEl().getValue();
18447         }
18448         
18449         this.doQuery(v);
18450     },
18451
18452     // private
18453     doForce : function(){
18454         if(this.inputEl().dom.value.length > 0){
18455             this.inputEl().dom.value =
18456                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18457              
18458         }
18459     },
18460
18461     /**
18462      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18463      * query allowing the query action to be canceled if needed.
18464      * @param {String} query The SQL query to execute
18465      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18466      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18467      * saved in the current store (defaults to false)
18468      */
18469     doQuery : function(q, forceAll){
18470         
18471         if(q === undefined || q === null){
18472             q = '';
18473         }
18474         var qe = {
18475             query: q,
18476             forceAll: forceAll,
18477             combo: this,
18478             cancel:false
18479         };
18480         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18481             return false;
18482         }
18483         q = qe.query;
18484         
18485         forceAll = qe.forceAll;
18486         if(forceAll === true || (q.length >= this.minChars)){
18487             
18488             this.hasQuery = true;
18489             
18490             if(this.lastQuery != q || this.alwaysQuery){
18491                 this.lastQuery = q;
18492                 if(this.mode == 'local'){
18493                     this.selectedIndex = -1;
18494                     if(forceAll){
18495                         this.store.clearFilter();
18496                     }else{
18497                         
18498                         if(this.specialFilter){
18499                             this.fireEvent('specialfilter', this);
18500                             this.onLoad();
18501                             return;
18502                         }
18503                         
18504                         this.store.filter(this.displayField, q);
18505                     }
18506                     
18507                     this.store.fireEvent("datachanged", this.store);
18508                     
18509                     this.onLoad();
18510                     
18511                     
18512                 }else{
18513                     
18514                     this.store.baseParams[this.queryParam] = q;
18515                     
18516                     var options = {params : this.getParams(q)};
18517                     
18518                     if(this.loadNext){
18519                         options.add = true;
18520                         options.params.start = this.page * this.pageSize;
18521                     }
18522                     
18523                     this.store.load(options);
18524                     
18525                     /*
18526                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18527                      *  we should expand the list on onLoad
18528                      *  so command out it
18529                      */
18530 //                    this.expand();
18531                 }
18532             }else{
18533                 this.selectedIndex = -1;
18534                 this.onLoad();   
18535             }
18536         }
18537         
18538         this.loadNext = false;
18539     },
18540     
18541     // private
18542     getParams : function(q){
18543         var p = {};
18544         //p[this.queryParam] = q;
18545         
18546         if(this.pageSize){
18547             p.start = 0;
18548             p.limit = this.pageSize;
18549         }
18550         return p;
18551     },
18552
18553     /**
18554      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18555      */
18556     collapse : function(){
18557         if(!this.isExpanded()){
18558             return;
18559         }
18560         
18561         this.list.hide();
18562         
18563         this.hasFocus = false;
18564         
18565         if(this.tickable){
18566             this.okBtn.hide();
18567             this.cancelBtn.hide();
18568             this.trigger.show();
18569             
18570             if(this.editable){
18571                 this.tickableInputEl().dom.value = '';
18572                 this.tickableInputEl().blur();
18573             }
18574             
18575         }
18576         
18577         Roo.get(document).un('mousedown', this.collapseIf, this);
18578         Roo.get(document).un('mousewheel', this.collapseIf, this);
18579         if (!this.editable) {
18580             Roo.get(document).un('keydown', this.listKeyPress, this);
18581         }
18582         this.fireEvent('collapse', this);
18583         
18584         this.validate();
18585     },
18586
18587     // private
18588     collapseIf : function(e){
18589         var in_combo  = e.within(this.el);
18590         var in_list =  e.within(this.list);
18591         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18592         
18593         if (in_combo || in_list || is_list) {
18594             //e.stopPropagation();
18595             return;
18596         }
18597         
18598         if(this.tickable){
18599             this.onTickableFooterButtonClick(e, false, false);
18600         }
18601
18602         this.collapse();
18603         
18604     },
18605
18606     /**
18607      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18608      */
18609     expand : function(){
18610        
18611         if(this.isExpanded() || !this.hasFocus){
18612             return;
18613         }
18614         
18615         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18616         this.list.setWidth(lw);
18617         
18618         Roo.log('expand');
18619         
18620         this.list.show();
18621         
18622         this.restrictHeight();
18623         
18624         if(this.tickable){
18625             
18626             this.tickItems = Roo.apply([], this.item);
18627             
18628             this.okBtn.show();
18629             this.cancelBtn.show();
18630             this.trigger.hide();
18631             
18632             if(this.editable){
18633                 this.tickableInputEl().focus();
18634             }
18635             
18636         }
18637         
18638         Roo.get(document).on('mousedown', this.collapseIf, this);
18639         Roo.get(document).on('mousewheel', this.collapseIf, this);
18640         if (!this.editable) {
18641             Roo.get(document).on('keydown', this.listKeyPress, this);
18642         }
18643         
18644         this.fireEvent('expand', this);
18645     },
18646
18647     // private
18648     // Implements the default empty TriggerField.onTriggerClick function
18649     onTriggerClick : function(e)
18650     {
18651         Roo.log('trigger click');
18652         
18653         if(this.disabled || !this.triggerList){
18654             return;
18655         }
18656         
18657         this.page = 0;
18658         this.loadNext = false;
18659         
18660         if(this.isExpanded()){
18661             this.collapse();
18662             if (!this.blockFocus) {
18663                 this.inputEl().focus();
18664             }
18665             
18666         }else {
18667             this.hasFocus = true;
18668             if(this.triggerAction == 'all') {
18669                 this.doQuery(this.allQuery, true);
18670             } else {
18671                 this.doQuery(this.getRawValue());
18672             }
18673             if (!this.blockFocus) {
18674                 this.inputEl().focus();
18675             }
18676         }
18677     },
18678     
18679     onTickableTriggerClick : function(e)
18680     {
18681         if(this.disabled){
18682             return;
18683         }
18684         
18685         this.page = 0;
18686         this.loadNext = false;
18687         this.hasFocus = true;
18688         
18689         if(this.triggerAction == 'all') {
18690             this.doQuery(this.allQuery, true);
18691         } else {
18692             this.doQuery(this.getRawValue());
18693         }
18694     },
18695     
18696     onSearchFieldClick : function(e)
18697     {
18698         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18699             this.onTickableFooterButtonClick(e, false, false);
18700             return;
18701         }
18702         
18703         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18704             return;
18705         }
18706         
18707         this.page = 0;
18708         this.loadNext = false;
18709         this.hasFocus = true;
18710         
18711         if(this.triggerAction == 'all') {
18712             this.doQuery(this.allQuery, true);
18713         } else {
18714             this.doQuery(this.getRawValue());
18715         }
18716     },
18717     
18718     listKeyPress : function(e)
18719     {
18720         //Roo.log('listkeypress');
18721         // scroll to first matching element based on key pres..
18722         if (e.isSpecialKey()) {
18723             return false;
18724         }
18725         var k = String.fromCharCode(e.getKey()).toUpperCase();
18726         //Roo.log(k);
18727         var match  = false;
18728         var csel = this.view.getSelectedNodes();
18729         var cselitem = false;
18730         if (csel.length) {
18731             var ix = this.view.indexOf(csel[0]);
18732             cselitem  = this.store.getAt(ix);
18733             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18734                 cselitem = false;
18735             }
18736             
18737         }
18738         
18739         this.store.each(function(v) { 
18740             if (cselitem) {
18741                 // start at existing selection.
18742                 if (cselitem.id == v.id) {
18743                     cselitem = false;
18744                 }
18745                 return true;
18746             }
18747                 
18748             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18749                 match = this.store.indexOf(v);
18750                 return false;
18751             }
18752             return true;
18753         }, this);
18754         
18755         if (match === false) {
18756             return true; // no more action?
18757         }
18758         // scroll to?
18759         this.view.select(match);
18760         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18761         sn.scrollIntoView(sn.dom.parentNode, false);
18762     },
18763     
18764     onViewScroll : function(e, t){
18765         
18766         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){
18767             return;
18768         }
18769         
18770         this.hasQuery = true;
18771         
18772         this.loading = this.list.select('.loading', true).first();
18773         
18774         if(this.loading === null){
18775             this.list.createChild({
18776                 tag: 'div',
18777                 cls: 'loading roo-select2-more-results roo-select2-active',
18778                 html: 'Loading more results...'
18779             });
18780             
18781             this.loading = this.list.select('.loading', true).first();
18782             
18783             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18784             
18785             this.loading.hide();
18786         }
18787         
18788         this.loading.show();
18789         
18790         var _combo = this;
18791         
18792         this.page++;
18793         this.loadNext = true;
18794         
18795         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18796         
18797         return;
18798     },
18799     
18800     addItem : function(o)
18801     {   
18802         var dv = ''; // display value
18803         
18804         if (this.displayField) {
18805             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18806         } else {
18807             // this is an error condition!!!
18808             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18809         }
18810         
18811         if(!dv.length){
18812             return;
18813         }
18814         
18815         var choice = this.choices.createChild({
18816             tag: 'li',
18817             cls: 'roo-select2-search-choice',
18818             cn: [
18819                 {
18820                     tag: 'div',
18821                     html: dv
18822                 },
18823                 {
18824                     tag: 'a',
18825                     href: '#',
18826                     cls: 'roo-select2-search-choice-close fa fa-times',
18827                     tabindex: '-1'
18828                 }
18829             ]
18830             
18831         }, this.searchField);
18832         
18833         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18834         
18835         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18836         
18837         this.item.push(o);
18838         
18839         this.lastData = o;
18840         
18841         this.syncValue();
18842         
18843         this.inputEl().dom.value = '';
18844         
18845         this.validate();
18846     },
18847     
18848     onRemoveItem : function(e, _self, o)
18849     {
18850         e.preventDefault();
18851         
18852         this.lastItem = Roo.apply([], this.item);
18853         
18854         var index = this.item.indexOf(o.data) * 1;
18855         
18856         if( index < 0){
18857             Roo.log('not this item?!');
18858             return;
18859         }
18860         
18861         this.item.splice(index, 1);
18862         o.item.remove();
18863         
18864         this.syncValue();
18865         
18866         this.fireEvent('remove', this, e);
18867         
18868         this.validate();
18869         
18870     },
18871     
18872     syncValue : function()
18873     {
18874         if(!this.item.length){
18875             this.clearValue();
18876             return;
18877         }
18878             
18879         var value = [];
18880         var _this = this;
18881         Roo.each(this.item, function(i){
18882             if(_this.valueField){
18883                 value.push(i[_this.valueField]);
18884                 return;
18885             }
18886
18887             value.push(i);
18888         });
18889
18890         this.value = value.join(',');
18891
18892         if(this.hiddenField){
18893             this.hiddenField.dom.value = this.value;
18894         }
18895         
18896         this.store.fireEvent("datachanged", this.store);
18897         
18898         this.validate();
18899     },
18900     
18901     clearItem : function()
18902     {
18903         if(!this.multiple){
18904             return;
18905         }
18906         
18907         this.item = [];
18908         
18909         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18910            c.remove();
18911         });
18912         
18913         this.syncValue();
18914         
18915         this.validate();
18916         
18917         if(this.tickable && !Roo.isTouch){
18918             this.view.refresh();
18919         }
18920     },
18921     
18922     inputEl: function ()
18923     {
18924         if(Roo.isIOS && this.useNativeIOS){
18925             return this.el.select('select.roo-ios-select', true).first();
18926         }
18927         
18928         if(Roo.isTouch && this.mobileTouchView){
18929             return this.el.select('input.form-control',true).first();
18930         }
18931         
18932         if(this.tickable){
18933             return this.searchField;
18934         }
18935         
18936         return this.el.select('input.form-control',true).first();
18937     },
18938     
18939     onTickableFooterButtonClick : function(e, btn, el)
18940     {
18941         e.preventDefault();
18942         
18943         this.lastItem = Roo.apply([], this.item);
18944         
18945         if(btn && btn.name == 'cancel'){
18946             this.tickItems = Roo.apply([], this.item);
18947             this.collapse();
18948             return;
18949         }
18950         
18951         this.clearItem();
18952         
18953         var _this = this;
18954         
18955         Roo.each(this.tickItems, function(o){
18956             _this.addItem(o);
18957         });
18958         
18959         this.collapse();
18960         
18961     },
18962     
18963     validate : function()
18964     {
18965         if(this.getVisibilityEl().hasClass('hidden')){
18966             return true;
18967         }
18968         
18969         var v = this.getRawValue();
18970         
18971         if(this.multiple){
18972             v = this.getValue();
18973         }
18974         
18975         if(this.disabled || this.allowBlank || v.length){
18976             this.markValid();
18977             return true;
18978         }
18979         
18980         this.markInvalid();
18981         return false;
18982     },
18983     
18984     tickableInputEl : function()
18985     {
18986         if(!this.tickable || !this.editable){
18987             return this.inputEl();
18988         }
18989         
18990         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18991     },
18992     
18993     
18994     getAutoCreateTouchView : function()
18995     {
18996         var id = Roo.id();
18997         
18998         var cfg = {
18999             cls: 'form-group' //input-group
19000         };
19001         
19002         var input =  {
19003             tag: 'input',
19004             id : id,
19005             type : this.inputType,
19006             cls : 'form-control x-combo-noedit',
19007             autocomplete: 'new-password',
19008             placeholder : this.placeholder || '',
19009             readonly : true
19010         };
19011         
19012         if (this.name) {
19013             input.name = this.name;
19014         }
19015         
19016         if (this.size) {
19017             input.cls += ' input-' + this.size;
19018         }
19019         
19020         if (this.disabled) {
19021             input.disabled = true;
19022         }
19023         
19024         var inputblock = {
19025             cls : 'roo-combobox-wrap',
19026             cn : [
19027                 input
19028             ]
19029         };
19030         
19031         if(this.before){
19032             inputblock.cls += ' input-group';
19033             
19034             inputblock.cn.unshift({
19035                 tag :'span',
19036                 cls : 'input-group-addon input-group-prepend input-group-text',
19037                 html : this.before
19038             });
19039         }
19040         
19041         if(this.removable && !this.multiple){
19042             inputblock.cls += ' roo-removable';
19043             
19044             inputblock.cn.push({
19045                 tag: 'button',
19046                 html : 'x',
19047                 cls : 'roo-combo-removable-btn close'
19048             });
19049         }
19050
19051         if(this.hasFeedback && !this.allowBlank){
19052             
19053             inputblock.cls += ' has-feedback';
19054             
19055             inputblock.cn.push({
19056                 tag: 'span',
19057                 cls: 'glyphicon form-control-feedback'
19058             });
19059             
19060         }
19061         
19062         if (this.after) {
19063             
19064             inputblock.cls += (this.before) ? '' : ' input-group';
19065             
19066             inputblock.cn.push({
19067                 tag :'span',
19068                 cls : 'input-group-addon input-group-append input-group-text',
19069                 html : this.after
19070             });
19071         }
19072
19073         
19074         var ibwrap = inputblock;
19075         
19076         if(this.multiple){
19077             ibwrap = {
19078                 tag: 'ul',
19079                 cls: 'roo-select2-choices',
19080                 cn:[
19081                     {
19082                         tag: 'li',
19083                         cls: 'roo-select2-search-field',
19084                         cn: [
19085
19086                             inputblock
19087                         ]
19088                     }
19089                 ]
19090             };
19091         
19092             
19093         }
19094         
19095         var combobox = {
19096             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19097             cn: [
19098                 {
19099                     tag: 'input',
19100                     type : 'hidden',
19101                     cls: 'form-hidden-field'
19102                 },
19103                 ibwrap
19104             ]
19105         };
19106         
19107         if(!this.multiple && this.showToggleBtn){
19108             
19109             var caret = {
19110                 cls: 'caret'
19111             };
19112             
19113             if (this.caret != false) {
19114                 caret = {
19115                      tag: 'i',
19116                      cls: 'fa fa-' + this.caret
19117                 };
19118                 
19119             }
19120             
19121             combobox.cn.push({
19122                 tag :'span',
19123                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19124                 cn : [
19125                     Roo.bootstrap.version == 3 ? caret : '',
19126                     {
19127                         tag: 'span',
19128                         cls: 'combobox-clear',
19129                         cn  : [
19130                             {
19131                                 tag : 'i',
19132                                 cls: 'icon-remove'
19133                             }
19134                         ]
19135                     }
19136                 ]
19137
19138             })
19139         }
19140         
19141         if(this.multiple){
19142             combobox.cls += ' roo-select2-container-multi';
19143         }
19144         
19145         var required =  this.allowBlank ?  {
19146                     tag : 'i',
19147                     style: 'display: none'
19148                 } : {
19149                    tag : 'i',
19150                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19151                    tooltip : 'This field is required'
19152                 };
19153         
19154         var align = this.labelAlign || this.parentLabelAlign();
19155         
19156         if (align ==='left' && this.fieldLabel.length) {
19157
19158             cfg.cn = [
19159                 required,
19160                 {
19161                     tag: 'label',
19162                     cls : 'control-label col-form-label',
19163                     html : this.fieldLabel
19164
19165                 },
19166                 {
19167                     cls : 'roo-combobox-wrap ', 
19168                     cn: [
19169                         combobox
19170                     ]
19171                 }
19172             ];
19173             
19174             var labelCfg = cfg.cn[1];
19175             var contentCfg = cfg.cn[2];
19176             
19177
19178             if(this.indicatorpos == 'right'){
19179                 cfg.cn = [
19180                     {
19181                         tag: 'label',
19182                         'for' :  id,
19183                         cls : 'control-label col-form-label',
19184                         cn : [
19185                             {
19186                                 tag : 'span',
19187                                 html : this.fieldLabel
19188                             },
19189                             required
19190                         ]
19191                     },
19192                     {
19193                         cls : "roo-combobox-wrap ",
19194                         cn: [
19195                             combobox
19196                         ]
19197                     }
19198
19199                 ];
19200                 
19201                 labelCfg = cfg.cn[0];
19202                 contentCfg = cfg.cn[1];
19203             }
19204             
19205            
19206             
19207             if(this.labelWidth > 12){
19208                 labelCfg.style = "width: " + this.labelWidth + 'px';
19209             }
19210            
19211             if(this.labelWidth < 13 && this.labelmd == 0){
19212                 this.labelmd = this.labelWidth;
19213             }
19214             
19215             if(this.labellg > 0){
19216                 labelCfg.cls += ' col-lg-' + this.labellg;
19217                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19218             }
19219             
19220             if(this.labelmd > 0){
19221                 labelCfg.cls += ' col-md-' + this.labelmd;
19222                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19223             }
19224             
19225             if(this.labelsm > 0){
19226                 labelCfg.cls += ' col-sm-' + this.labelsm;
19227                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19228             }
19229             
19230             if(this.labelxs > 0){
19231                 labelCfg.cls += ' col-xs-' + this.labelxs;
19232                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19233             }
19234                 
19235                 
19236         } else if ( this.fieldLabel.length) {
19237             cfg.cn = [
19238                required,
19239                 {
19240                     tag: 'label',
19241                     cls : 'control-label',
19242                     html : this.fieldLabel
19243
19244                 },
19245                 {
19246                     cls : '', 
19247                     cn: [
19248                         combobox
19249                     ]
19250                 }
19251             ];
19252             
19253             if(this.indicatorpos == 'right'){
19254                 cfg.cn = [
19255                     {
19256                         tag: 'label',
19257                         cls : 'control-label',
19258                         html : this.fieldLabel,
19259                         cn : [
19260                             required
19261                         ]
19262                     },
19263                     {
19264                         cls : '', 
19265                         cn: [
19266                             combobox
19267                         ]
19268                     }
19269                 ];
19270             }
19271         } else {
19272             cfg.cn = combobox;    
19273         }
19274         
19275         
19276         var settings = this;
19277         
19278         ['xs','sm','md','lg'].map(function(size){
19279             if (settings[size]) {
19280                 cfg.cls += ' col-' + size + '-' + settings[size];
19281             }
19282         });
19283         
19284         return cfg;
19285     },
19286     
19287     initTouchView : function()
19288     {
19289         this.renderTouchView();
19290         
19291         this.touchViewEl.on('scroll', function(){
19292             this.el.dom.scrollTop = 0;
19293         }, this);
19294         
19295         this.originalValue = this.getValue();
19296         
19297         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19298         
19299         this.inputEl().on("click", this.showTouchView, this);
19300         if (this.triggerEl) {
19301             this.triggerEl.on("click", this.showTouchView, this);
19302         }
19303         
19304         
19305         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19306         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19307         
19308         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19309         
19310         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19311         this.store.on('load', this.onTouchViewLoad, this);
19312         this.store.on('loadexception', this.onTouchViewLoadException, this);
19313         
19314         if(this.hiddenName){
19315             
19316             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19317             
19318             this.hiddenField.dom.value =
19319                 this.hiddenValue !== undefined ? this.hiddenValue :
19320                 this.value !== undefined ? this.value : '';
19321         
19322             this.el.dom.removeAttribute('name');
19323             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19324         }
19325         
19326         if(this.multiple){
19327             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19328             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19329         }
19330         
19331         if(this.removable && !this.multiple){
19332             var close = this.closeTriggerEl();
19333             if(close){
19334                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19335                 close.on('click', this.removeBtnClick, this, close);
19336             }
19337         }
19338         /*
19339          * fix the bug in Safari iOS8
19340          */
19341         this.inputEl().on("focus", function(e){
19342             document.activeElement.blur();
19343         }, this);
19344         
19345         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19346         
19347         return;
19348         
19349         
19350     },
19351     
19352     renderTouchView : function()
19353     {
19354         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19355         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19356         
19357         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19358         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19359         
19360         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19361         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19362         this.touchViewBodyEl.setStyle('overflow', 'auto');
19363         
19364         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19365         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         
19367         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19368         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370     },
19371     
19372     showTouchView : function()
19373     {
19374         if(this.disabled){
19375             return;
19376         }
19377         
19378         this.touchViewHeaderEl.hide();
19379
19380         if(this.modalTitle.length){
19381             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19382             this.touchViewHeaderEl.show();
19383         }
19384
19385         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19386         this.touchViewEl.show();
19387
19388         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19389         
19390         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19391         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19392
19393         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19394
19395         if(this.modalTitle.length){
19396             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19397         }
19398         
19399         this.touchViewBodyEl.setHeight(bodyHeight);
19400
19401         if(this.animate){
19402             var _this = this;
19403             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19404         }else{
19405             this.touchViewEl.addClass(['in','show']);
19406         }
19407         
19408         if(this._touchViewMask){
19409             Roo.get(document.body).addClass("x-body-masked");
19410             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19411             this._touchViewMask.setStyle('z-index', 10000);
19412             this._touchViewMask.addClass('show');
19413         }
19414         
19415         this.doTouchViewQuery();
19416         
19417     },
19418     
19419     hideTouchView : function()
19420     {
19421         this.touchViewEl.removeClass(['in','show']);
19422
19423         if(this.animate){
19424             var _this = this;
19425             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19426         }else{
19427             this.touchViewEl.setStyle('display', 'none');
19428         }
19429         
19430         if(this._touchViewMask){
19431             this._touchViewMask.removeClass('show');
19432             Roo.get(document.body).removeClass("x-body-masked");
19433         }
19434     },
19435     
19436     setTouchViewValue : function()
19437     {
19438         if(this.multiple){
19439             this.clearItem();
19440         
19441             var _this = this;
19442
19443             Roo.each(this.tickItems, function(o){
19444                 this.addItem(o);
19445             }, this);
19446         }
19447         
19448         this.hideTouchView();
19449     },
19450     
19451     doTouchViewQuery : function()
19452     {
19453         var qe = {
19454             query: '',
19455             forceAll: true,
19456             combo: this,
19457             cancel:false
19458         };
19459         
19460         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19461             return false;
19462         }
19463         
19464         if(!this.alwaysQuery || this.mode == 'local'){
19465             this.onTouchViewLoad();
19466             return;
19467         }
19468         
19469         this.store.load();
19470     },
19471     
19472     onTouchViewBeforeLoad : function(combo,opts)
19473     {
19474         return;
19475     },
19476
19477     // private
19478     onTouchViewLoad : function()
19479     {
19480         if(this.store.getCount() < 1){
19481             this.onTouchViewEmptyResults();
19482             return;
19483         }
19484         
19485         this.clearTouchView();
19486         
19487         var rawValue = this.getRawValue();
19488         
19489         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19490         
19491         this.tickItems = [];
19492         
19493         this.store.data.each(function(d, rowIndex){
19494             var row = this.touchViewListGroup.createChild(template);
19495             
19496             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19497                 row.addClass(d.data.cls);
19498             }
19499             
19500             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19501                 var cfg = {
19502                     data : d.data,
19503                     html : d.data[this.displayField]
19504                 };
19505                 
19506                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19507                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19508                 }
19509             }
19510             row.removeClass('selected');
19511             if(!this.multiple && this.valueField &&
19512                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19513             {
19514                 // radio buttons..
19515                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19516                 row.addClass('selected');
19517             }
19518             
19519             if(this.multiple && this.valueField &&
19520                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19521             {
19522                 
19523                 // checkboxes...
19524                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19525                 this.tickItems.push(d.data);
19526             }
19527             
19528             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19529             
19530         }, this);
19531         
19532         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19533         
19534         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19535
19536         if(this.modalTitle.length){
19537             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19538         }
19539
19540         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19541         
19542         if(this.mobile_restrict_height && listHeight < bodyHeight){
19543             this.touchViewBodyEl.setHeight(listHeight);
19544         }
19545         
19546         var _this = this;
19547         
19548         if(firstChecked && listHeight > bodyHeight){
19549             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19550         }
19551         
19552     },
19553     
19554     onTouchViewLoadException : function()
19555     {
19556         this.hideTouchView();
19557     },
19558     
19559     onTouchViewEmptyResults : function()
19560     {
19561         this.clearTouchView();
19562         
19563         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19564         
19565         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19566         
19567     },
19568     
19569     clearTouchView : function()
19570     {
19571         this.touchViewListGroup.dom.innerHTML = '';
19572     },
19573     
19574     onTouchViewClick : function(e, el, o)
19575     {
19576         e.preventDefault();
19577         
19578         var row = o.row;
19579         var rowIndex = o.rowIndex;
19580         
19581         var r = this.store.getAt(rowIndex);
19582         
19583         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19584             
19585             if(!this.multiple){
19586                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19587                     c.dom.removeAttribute('checked');
19588                 }, this);
19589
19590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19591
19592                 this.setFromData(r.data);
19593
19594                 var close = this.closeTriggerEl();
19595
19596                 if(close){
19597                     close.show();
19598                 }
19599
19600                 this.hideTouchView();
19601
19602                 this.fireEvent('select', this, r, rowIndex);
19603
19604                 return;
19605             }
19606
19607             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19608                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19609                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19610                 return;
19611             }
19612
19613             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19614             this.addItem(r.data);
19615             this.tickItems.push(r.data);
19616         }
19617     },
19618     
19619     getAutoCreateNativeIOS : function()
19620     {
19621         var cfg = {
19622             cls: 'form-group' //input-group,
19623         };
19624         
19625         var combobox =  {
19626             tag: 'select',
19627             cls : 'roo-ios-select'
19628         };
19629         
19630         if (this.name) {
19631             combobox.name = this.name;
19632         }
19633         
19634         if (this.disabled) {
19635             combobox.disabled = true;
19636         }
19637         
19638         var settings = this;
19639         
19640         ['xs','sm','md','lg'].map(function(size){
19641             if (settings[size]) {
19642                 cfg.cls += ' col-' + size + '-' + settings[size];
19643             }
19644         });
19645         
19646         cfg.cn = combobox;
19647         
19648         return cfg;
19649         
19650     },
19651     
19652     initIOSView : function()
19653     {
19654         this.store.on('load', this.onIOSViewLoad, this);
19655         
19656         return;
19657     },
19658     
19659     onIOSViewLoad : function()
19660     {
19661         if(this.store.getCount() < 1){
19662             return;
19663         }
19664         
19665         this.clearIOSView();
19666         
19667         if(this.allowBlank) {
19668             
19669             var default_text = '-- SELECT --';
19670             
19671             if(this.placeholder.length){
19672                 default_text = this.placeholder;
19673             }
19674             
19675             if(this.emptyTitle.length){
19676                 default_text += ' - ' + this.emptyTitle + ' -';
19677             }
19678             
19679             var opt = this.inputEl().createChild({
19680                 tag: 'option',
19681                 value : 0,
19682                 html : default_text
19683             });
19684             
19685             var o = {};
19686             o[this.valueField] = 0;
19687             o[this.displayField] = default_text;
19688             
19689             this.ios_options.push({
19690                 data : o,
19691                 el : opt
19692             });
19693             
19694         }
19695         
19696         this.store.data.each(function(d, rowIndex){
19697             
19698             var html = '';
19699             
19700             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19701                 html = d.data[this.displayField];
19702             }
19703             
19704             var value = '';
19705             
19706             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19707                 value = d.data[this.valueField];
19708             }
19709             
19710             var option = {
19711                 tag: 'option',
19712                 value : value,
19713                 html : html
19714             };
19715             
19716             if(this.value == d.data[this.valueField]){
19717                 option['selected'] = true;
19718             }
19719             
19720             var opt = this.inputEl().createChild(option);
19721             
19722             this.ios_options.push({
19723                 data : d.data,
19724                 el : opt
19725             });
19726             
19727         }, this);
19728         
19729         this.inputEl().on('change', function(){
19730            this.fireEvent('select', this);
19731         }, this);
19732         
19733     },
19734     
19735     clearIOSView: function()
19736     {
19737         this.inputEl().dom.innerHTML = '';
19738         
19739         this.ios_options = [];
19740     },
19741     
19742     setIOSValue: function(v)
19743     {
19744         this.value = v;
19745         
19746         if(!this.ios_options){
19747             return;
19748         }
19749         
19750         Roo.each(this.ios_options, function(opts){
19751            
19752            opts.el.dom.removeAttribute('selected');
19753            
19754            if(opts.data[this.valueField] != v){
19755                return;
19756            }
19757            
19758            opts.el.dom.setAttribute('selected', true);
19759            
19760         }, this);
19761     }
19762
19763     /** 
19764     * @cfg {Boolean} grow 
19765     * @hide 
19766     */
19767     /** 
19768     * @cfg {Number} growMin 
19769     * @hide 
19770     */
19771     /** 
19772     * @cfg {Number} growMax 
19773     * @hide 
19774     */
19775     /**
19776      * @hide
19777      * @method autoSize
19778      */
19779 });
19780
19781 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19782     
19783     header : {
19784         tag: 'div',
19785         cls: 'modal-header',
19786         cn: [
19787             {
19788                 tag: 'h4',
19789                 cls: 'modal-title'
19790             }
19791         ]
19792     },
19793     
19794     body : {
19795         tag: 'div',
19796         cls: 'modal-body',
19797         cn: [
19798             {
19799                 tag: 'ul',
19800                 cls: 'list-group'
19801             }
19802         ]
19803     },
19804     
19805     listItemRadio : {
19806         tag: 'li',
19807         cls: 'list-group-item',
19808         cn: [
19809             {
19810                 tag: 'span',
19811                 cls: 'roo-combobox-list-group-item-value'
19812             },
19813             {
19814                 tag: 'div',
19815                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19816                 cn: [
19817                     {
19818                         tag: 'input',
19819                         type: 'radio'
19820                     },
19821                     {
19822                         tag: 'label'
19823                     }
19824                 ]
19825             }
19826         ]
19827     },
19828     
19829     listItemCheckbox : {
19830         tag: 'li',
19831         cls: 'list-group-item',
19832         cn: [
19833             {
19834                 tag: 'span',
19835                 cls: 'roo-combobox-list-group-item-value'
19836             },
19837             {
19838                 tag: 'div',
19839                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19840                 cn: [
19841                     {
19842                         tag: 'input',
19843                         type: 'checkbox'
19844                     },
19845                     {
19846                         tag: 'label'
19847                     }
19848                 ]
19849             }
19850         ]
19851     },
19852     
19853     emptyResult : {
19854         tag: 'div',
19855         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19856     },
19857     
19858     footer : {
19859         tag: 'div',
19860         cls: 'modal-footer',
19861         cn: [
19862             {
19863                 tag: 'div',
19864                 cls: 'row',
19865                 cn: [
19866                     {
19867                         tag: 'div',
19868                         cls: 'col-xs-6 text-left',
19869                         cn: {
19870                             tag: 'button',
19871                             cls: 'btn btn-danger roo-touch-view-cancel',
19872                             html: 'Cancel'
19873                         }
19874                     },
19875                     {
19876                         tag: 'div',
19877                         cls: 'col-xs-6 text-right',
19878                         cn: {
19879                             tag: 'button',
19880                             cls: 'btn btn-success roo-touch-view-ok',
19881                             html: 'OK'
19882                         }
19883                     }
19884                 ]
19885             }
19886         ]
19887         
19888     }
19889 });
19890
19891 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19892     
19893     touchViewTemplate : {
19894         tag: 'div',
19895         cls: 'modal fade roo-combobox-touch-view',
19896         cn: [
19897             {
19898                 tag: 'div',
19899                 cls: 'modal-dialog',
19900                 style : 'position:fixed', // we have to fix position....
19901                 cn: [
19902                     {
19903                         tag: 'div',
19904                         cls: 'modal-content',
19905                         cn: [
19906                             Roo.bootstrap.form.ComboBox.header,
19907                             Roo.bootstrap.form.ComboBox.body,
19908                             Roo.bootstrap.form.ComboBox.footer
19909                         ]
19910                     }
19911                 ]
19912             }
19913         ]
19914     }
19915 });/*
19916  * Based on:
19917  * Ext JS Library 1.1.1
19918  * Copyright(c) 2006-2007, Ext JS, LLC.
19919  *
19920  * Originally Released Under LGPL - original licence link has changed is not relivant.
19921  *
19922  * Fork - LGPL
19923  * <script type="text/javascript">
19924  */
19925
19926 /**
19927  * @class Roo.View
19928  * @extends Roo.util.Observable
19929  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19930  * This class also supports single and multi selection modes. <br>
19931  * Create a data model bound view:
19932  <pre><code>
19933  var store = new Roo.data.Store(...);
19934
19935  var view = new Roo.View({
19936     el : "my-element",
19937     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19938  
19939     singleSelect: true,
19940     selectedClass: "ydataview-selected",
19941     store: store
19942  });
19943
19944  // listen for node click?
19945  view.on("click", function(vw, index, node, e){
19946  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19947  });
19948
19949  // load XML data
19950  dataModel.load("foobar.xml");
19951  </code></pre>
19952  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19953  * <br><br>
19954  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19955  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19956  * 
19957  * Note: old style constructor is still suported (container, template, config)
19958  * 
19959  * @constructor
19960  * Create a new View
19961  * @param {Object} config The config object
19962  * 
19963  */
19964 Roo.View = function(config, depreciated_tpl, depreciated_config){
19965     
19966     this.parent = false;
19967     
19968     if (typeof(depreciated_tpl) == 'undefined') {
19969         // new way.. - universal constructor.
19970         Roo.apply(this, config);
19971         this.el  = Roo.get(this.el);
19972     } else {
19973         // old format..
19974         this.el  = Roo.get(config);
19975         this.tpl = depreciated_tpl;
19976         Roo.apply(this, depreciated_config);
19977     }
19978     this.wrapEl  = this.el.wrap().wrap();
19979     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19980     
19981     
19982     if(typeof(this.tpl) == "string"){
19983         this.tpl = new Roo.Template(this.tpl);
19984     } else {
19985         // support xtype ctors..
19986         this.tpl = new Roo.factory(this.tpl, Roo);
19987     }
19988     
19989     
19990     this.tpl.compile();
19991     
19992     /** @private */
19993     this.addEvents({
19994         /**
19995          * @event beforeclick
19996          * Fires before a click is processed. Returns false to cancel the default action.
19997          * @param {Roo.View} this
19998          * @param {Number} index The index of the target node
19999          * @param {HTMLElement} node The target node
20000          * @param {Roo.EventObject} e The raw event object
20001          */
20002             "beforeclick" : true,
20003         /**
20004          * @event click
20005          * Fires when a template node is clicked.
20006          * @param {Roo.View} this
20007          * @param {Number} index The index of the target node
20008          * @param {HTMLElement} node The target node
20009          * @param {Roo.EventObject} e The raw event object
20010          */
20011             "click" : true,
20012         /**
20013          * @event dblclick
20014          * Fires when a template node is double clicked.
20015          * @param {Roo.View} this
20016          * @param {Number} index The index of the target node
20017          * @param {HTMLElement} node The target node
20018          * @param {Roo.EventObject} e The raw event object
20019          */
20020             "dblclick" : true,
20021         /**
20022          * @event contextmenu
20023          * Fires when a template node is right clicked.
20024          * @param {Roo.View} this
20025          * @param {Number} index The index of the target node
20026          * @param {HTMLElement} node The target node
20027          * @param {Roo.EventObject} e The raw event object
20028          */
20029             "contextmenu" : true,
20030         /**
20031          * @event selectionchange
20032          * Fires when the selected nodes change.
20033          * @param {Roo.View} this
20034          * @param {Array} selections Array of the selected nodes
20035          */
20036             "selectionchange" : true,
20037     
20038         /**
20039          * @event beforeselect
20040          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20041          * @param {Roo.View} this
20042          * @param {HTMLElement} node The node to be selected
20043          * @param {Array} selections Array of currently selected nodes
20044          */
20045             "beforeselect" : true,
20046         /**
20047          * @event preparedata
20048          * Fires on every row to render, to allow you to change the data.
20049          * @param {Roo.View} this
20050          * @param {Object} data to be rendered (change this)
20051          */
20052           "preparedata" : true
20053           
20054           
20055         });
20056
20057
20058
20059     this.el.on({
20060         "click": this.onClick,
20061         "dblclick": this.onDblClick,
20062         "contextmenu": this.onContextMenu,
20063         scope:this
20064     });
20065
20066     this.selections = [];
20067     this.nodes = [];
20068     this.cmp = new Roo.CompositeElementLite([]);
20069     if(this.store){
20070         this.store = Roo.factory(this.store, Roo.data);
20071         this.setStore(this.store, true);
20072     }
20073     
20074     if ( this.footer && this.footer.xtype) {
20075            
20076          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20077         
20078         this.footer.dataSource = this.store;
20079         this.footer.container = fctr;
20080         this.footer = Roo.factory(this.footer, Roo);
20081         fctr.insertFirst(this.el);
20082         
20083         // this is a bit insane - as the paging toolbar seems to detach the el..
20084 //        dom.parentNode.parentNode.parentNode
20085          // they get detached?
20086     }
20087     
20088     
20089     Roo.View.superclass.constructor.call(this);
20090     
20091     
20092 };
20093
20094 Roo.extend(Roo.View, Roo.util.Observable, {
20095     
20096      /**
20097      * @cfg {Roo.data.Store} store Data store to load data from.
20098      */
20099     store : false,
20100     
20101     /**
20102      * @cfg {String|Roo.Element} el The container element.
20103      */
20104     el : '',
20105     
20106     /**
20107      * @cfg {String|Roo.Template} tpl The template used by this View 
20108      */
20109     tpl : false,
20110     /**
20111      * @cfg {String} dataName the named area of the template to use as the data area
20112      *                          Works with domtemplates roo-name="name"
20113      */
20114     dataName: false,
20115     /**
20116      * @cfg {String} selectedClass The css class to add to selected nodes
20117      */
20118     selectedClass : "x-view-selected",
20119      /**
20120      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20121      */
20122     emptyText : "",
20123     
20124     /**
20125      * @cfg {String} text to display on mask (default Loading)
20126      */
20127     mask : false,
20128     /**
20129      * @cfg {Boolean} multiSelect Allow multiple selection
20130      */
20131     multiSelect : false,
20132     /**
20133      * @cfg {Boolean} singleSelect Allow single selection
20134      */
20135     singleSelect:  false,
20136     
20137     /**
20138      * @cfg {Boolean} toggleSelect - selecting 
20139      */
20140     toggleSelect : false,
20141     
20142     /**
20143      * @cfg {Boolean} tickable - selecting 
20144      */
20145     tickable : false,
20146     
20147     /**
20148      * Returns the element this view is bound to.
20149      * @return {Roo.Element}
20150      */
20151     getEl : function(){
20152         return this.wrapEl;
20153     },
20154     
20155     
20156
20157     /**
20158      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20159      */
20160     refresh : function(){
20161         //Roo.log('refresh');
20162         var t = this.tpl;
20163         
20164         // if we are using something like 'domtemplate', then
20165         // the what gets used is:
20166         // t.applySubtemplate(NAME, data, wrapping data..)
20167         // the outer template then get' applied with
20168         //     the store 'extra data'
20169         // and the body get's added to the
20170         //      roo-name="data" node?
20171         //      <span class='roo-tpl-{name}'></span> ?????
20172         
20173         
20174         
20175         this.clearSelections();
20176         this.el.update("");
20177         var html = [];
20178         var records = this.store.getRange();
20179         if(records.length < 1) {
20180             
20181             // is this valid??  = should it render a template??
20182             
20183             this.el.update(this.emptyText);
20184             return;
20185         }
20186         var el = this.el;
20187         if (this.dataName) {
20188             this.el.update(t.apply(this.store.meta)); //????
20189             el = this.el.child('.roo-tpl-' + this.dataName);
20190         }
20191         
20192         for(var i = 0, len = records.length; i < len; i++){
20193             var data = this.prepareData(records[i].data, i, records[i]);
20194             this.fireEvent("preparedata", this, data, i, records[i]);
20195             
20196             var d = Roo.apply({}, data);
20197             
20198             if(this.tickable){
20199                 Roo.apply(d, {'roo-id' : Roo.id()});
20200                 
20201                 var _this = this;
20202             
20203                 Roo.each(this.parent.item, function(item){
20204                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20205                         return;
20206                     }
20207                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20208                 });
20209             }
20210             
20211             html[html.length] = Roo.util.Format.trim(
20212                 this.dataName ?
20213                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20214                     t.apply(d)
20215             );
20216         }
20217         
20218         
20219         
20220         el.update(html.join(""));
20221         this.nodes = el.dom.childNodes;
20222         this.updateIndexes(0);
20223     },
20224     
20225
20226     /**
20227      * Function to override to reformat the data that is sent to
20228      * the template for each node.
20229      * DEPRICATED - use the preparedata event handler.
20230      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20231      * a JSON object for an UpdateManager bound view).
20232      */
20233     prepareData : function(data, index, record)
20234     {
20235         this.fireEvent("preparedata", this, data, index, record);
20236         return data;
20237     },
20238
20239     onUpdate : function(ds, record){
20240         // Roo.log('on update');   
20241         this.clearSelections();
20242         var index = this.store.indexOf(record);
20243         var n = this.nodes[index];
20244         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20245         n.parentNode.removeChild(n);
20246         this.updateIndexes(index, index);
20247     },
20248
20249     
20250     
20251 // --------- FIXME     
20252     onAdd : function(ds, records, index)
20253     {
20254         //Roo.log(['on Add', ds, records, index] );        
20255         this.clearSelections();
20256         if(this.nodes.length == 0){
20257             this.refresh();
20258             return;
20259         }
20260         var n = this.nodes[index];
20261         for(var i = 0, len = records.length; i < len; i++){
20262             var d = this.prepareData(records[i].data, i, records[i]);
20263             if(n){
20264                 this.tpl.insertBefore(n, d);
20265             }else{
20266                 
20267                 this.tpl.append(this.el, d);
20268             }
20269         }
20270         this.updateIndexes(index);
20271     },
20272
20273     onRemove : function(ds, record, index){
20274        // Roo.log('onRemove');
20275         this.clearSelections();
20276         var el = this.dataName  ?
20277             this.el.child('.roo-tpl-' + this.dataName) :
20278             this.el; 
20279         
20280         el.dom.removeChild(this.nodes[index]);
20281         this.updateIndexes(index);
20282     },
20283
20284     /**
20285      * Refresh an individual node.
20286      * @param {Number} index
20287      */
20288     refreshNode : function(index){
20289         this.onUpdate(this.store, this.store.getAt(index));
20290     },
20291
20292     updateIndexes : function(startIndex, endIndex){
20293         var ns = this.nodes;
20294         startIndex = startIndex || 0;
20295         endIndex = endIndex || ns.length - 1;
20296         for(var i = startIndex; i <= endIndex; i++){
20297             ns[i].nodeIndex = i;
20298         }
20299     },
20300
20301     /**
20302      * Changes the data store this view uses and refresh the view.
20303      * @param {Store} store
20304      */
20305     setStore : function(store, initial){
20306         if(!initial && this.store){
20307             this.store.un("datachanged", this.refresh);
20308             this.store.un("add", this.onAdd);
20309             this.store.un("remove", this.onRemove);
20310             this.store.un("update", this.onUpdate);
20311             this.store.un("clear", this.refresh);
20312             this.store.un("beforeload", this.onBeforeLoad);
20313             this.store.un("load", this.onLoad);
20314             this.store.un("loadexception", this.onLoad);
20315         }
20316         if(store){
20317           
20318             store.on("datachanged", this.refresh, this);
20319             store.on("add", this.onAdd, this);
20320             store.on("remove", this.onRemove, this);
20321             store.on("update", this.onUpdate, this);
20322             store.on("clear", this.refresh, this);
20323             store.on("beforeload", this.onBeforeLoad, this);
20324             store.on("load", this.onLoad, this);
20325             store.on("loadexception", this.onLoad, this);
20326         }
20327         
20328         if(store){
20329             this.refresh();
20330         }
20331     },
20332     /**
20333      * onbeforeLoad - masks the loading area.
20334      *
20335      */
20336     onBeforeLoad : function(store,opts)
20337     {
20338          //Roo.log('onBeforeLoad');   
20339         if (!opts.add) {
20340             this.el.update("");
20341         }
20342         this.el.mask(this.mask ? this.mask : "Loading" ); 
20343     },
20344     onLoad : function ()
20345     {
20346         this.el.unmask();
20347     },
20348     
20349
20350     /**
20351      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20352      * @param {HTMLElement} node
20353      * @return {HTMLElement} The template node
20354      */
20355     findItemFromChild : function(node){
20356         var el = this.dataName  ?
20357             this.el.child('.roo-tpl-' + this.dataName,true) :
20358             this.el.dom; 
20359         
20360         if(!node || node.parentNode == el){
20361                     return node;
20362             }
20363             var p = node.parentNode;
20364             while(p && p != el){
20365             if(p.parentNode == el){
20366                 return p;
20367             }
20368             p = p.parentNode;
20369         }
20370             return null;
20371     },
20372
20373     /** @ignore */
20374     onClick : function(e){
20375         var item = this.findItemFromChild(e.getTarget());
20376         if(item){
20377             var index = this.indexOf(item);
20378             if(this.onItemClick(item, index, e) !== false){
20379                 this.fireEvent("click", this, index, item, e);
20380             }
20381         }else{
20382             this.clearSelections();
20383         }
20384     },
20385
20386     /** @ignore */
20387     onContextMenu : function(e){
20388         var item = this.findItemFromChild(e.getTarget());
20389         if(item){
20390             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20391         }
20392     },
20393
20394     /** @ignore */
20395     onDblClick : function(e){
20396         var item = this.findItemFromChild(e.getTarget());
20397         if(item){
20398             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20399         }
20400     },
20401
20402     onItemClick : function(item, index, e)
20403     {
20404         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20405             return false;
20406         }
20407         if (this.toggleSelect) {
20408             var m = this.isSelected(item) ? 'unselect' : 'select';
20409             //Roo.log(m);
20410             var _t = this;
20411             _t[m](item, true, false);
20412             return true;
20413         }
20414         if(this.multiSelect || this.singleSelect){
20415             if(this.multiSelect && e.shiftKey && this.lastSelection){
20416                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20417             }else{
20418                 this.select(item, this.multiSelect && e.ctrlKey);
20419                 this.lastSelection = item;
20420             }
20421             
20422             if(!this.tickable){
20423                 e.preventDefault();
20424             }
20425             
20426         }
20427         return true;
20428     },
20429
20430     /**
20431      * Get the number of selected nodes.
20432      * @return {Number}
20433      */
20434     getSelectionCount : function(){
20435         return this.selections.length;
20436     },
20437
20438     /**
20439      * Get the currently selected nodes.
20440      * @return {Array} An array of HTMLElements
20441      */
20442     getSelectedNodes : function(){
20443         return this.selections;
20444     },
20445
20446     /**
20447      * Get the indexes of the selected nodes.
20448      * @return {Array}
20449      */
20450     getSelectedIndexes : function(){
20451         var indexes = [], s = this.selections;
20452         for(var i = 0, len = s.length; i < len; i++){
20453             indexes.push(s[i].nodeIndex);
20454         }
20455         return indexes;
20456     },
20457
20458     /**
20459      * Clear all selections
20460      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20461      */
20462     clearSelections : function(suppressEvent){
20463         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20464             this.cmp.elements = this.selections;
20465             this.cmp.removeClass(this.selectedClass);
20466             this.selections = [];
20467             if(!suppressEvent){
20468                 this.fireEvent("selectionchange", this, this.selections);
20469             }
20470         }
20471     },
20472
20473     /**
20474      * Returns true if the passed node is selected
20475      * @param {HTMLElement/Number} node The node or node index
20476      * @return {Boolean}
20477      */
20478     isSelected : function(node){
20479         var s = this.selections;
20480         if(s.length < 1){
20481             return false;
20482         }
20483         node = this.getNode(node);
20484         return s.indexOf(node) !== -1;
20485     },
20486
20487     /**
20488      * Selects nodes.
20489      * @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
20490      * @param {Boolean} keepExisting (optional) true to keep existing selections
20491      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20492      */
20493     select : function(nodeInfo, keepExisting, suppressEvent){
20494         if(nodeInfo instanceof Array){
20495             if(!keepExisting){
20496                 this.clearSelections(true);
20497             }
20498             for(var i = 0, len = nodeInfo.length; i < len; i++){
20499                 this.select(nodeInfo[i], true, true);
20500             }
20501             return;
20502         } 
20503         var node = this.getNode(nodeInfo);
20504         if(!node || this.isSelected(node)){
20505             return; // already selected.
20506         }
20507         if(!keepExisting){
20508             this.clearSelections(true);
20509         }
20510         
20511         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20512             Roo.fly(node).addClass(this.selectedClass);
20513             this.selections.push(node);
20514             if(!suppressEvent){
20515                 this.fireEvent("selectionchange", this, this.selections);
20516             }
20517         }
20518         
20519         
20520     },
20521       /**
20522      * Unselects nodes.
20523      * @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
20524      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20525      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20526      */
20527     unselect : function(nodeInfo, keepExisting, suppressEvent)
20528     {
20529         if(nodeInfo instanceof Array){
20530             Roo.each(this.selections, function(s) {
20531                 this.unselect(s, nodeInfo);
20532             }, this);
20533             return;
20534         }
20535         var node = this.getNode(nodeInfo);
20536         if(!node || !this.isSelected(node)){
20537             //Roo.log("not selected");
20538             return; // not selected.
20539         }
20540         // fireevent???
20541         var ns = [];
20542         Roo.each(this.selections, function(s) {
20543             if (s == node ) {
20544                 Roo.fly(node).removeClass(this.selectedClass);
20545
20546                 return;
20547             }
20548             ns.push(s);
20549         },this);
20550         
20551         this.selections= ns;
20552         this.fireEvent("selectionchange", this, this.selections);
20553     },
20554
20555     /**
20556      * Gets a template node.
20557      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20558      * @return {HTMLElement} The node or null if it wasn't found
20559      */
20560     getNode : function(nodeInfo){
20561         if(typeof nodeInfo == "string"){
20562             return document.getElementById(nodeInfo);
20563         }else if(typeof nodeInfo == "number"){
20564             return this.nodes[nodeInfo];
20565         }
20566         return nodeInfo;
20567     },
20568
20569     /**
20570      * Gets a range template nodes.
20571      * @param {Number} startIndex
20572      * @param {Number} endIndex
20573      * @return {Array} An array of nodes
20574      */
20575     getNodes : function(start, end){
20576         var ns = this.nodes;
20577         start = start || 0;
20578         end = typeof end == "undefined" ? ns.length - 1 : end;
20579         var nodes = [];
20580         if(start <= end){
20581             for(var i = start; i <= end; i++){
20582                 nodes.push(ns[i]);
20583             }
20584         } else{
20585             for(var i = start; i >= end; i--){
20586                 nodes.push(ns[i]);
20587             }
20588         }
20589         return nodes;
20590     },
20591
20592     /**
20593      * Finds the index of the passed node
20594      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20595      * @return {Number} The index of the node or -1
20596      */
20597     indexOf : function(node){
20598         node = this.getNode(node);
20599         if(typeof node.nodeIndex == "number"){
20600             return node.nodeIndex;
20601         }
20602         var ns = this.nodes;
20603         for(var i = 0, len = ns.length; i < len; i++){
20604             if(ns[i] == node){
20605                 return i;
20606             }
20607         }
20608         return -1;
20609     }
20610 });
20611 /*
20612  * - LGPL
20613  *
20614  * based on jquery fullcalendar
20615  * 
20616  */
20617
20618 Roo.bootstrap = Roo.bootstrap || {};
20619 /**
20620  * @class Roo.bootstrap.Calendar
20621  * @extends Roo.bootstrap.Component
20622  * Bootstrap Calendar class
20623  * @cfg {Boolean} loadMask (true|false) default false
20624  * @cfg {Object} header generate the user specific header of the calendar, default false
20625
20626  * @constructor
20627  * Create a new Container
20628  * @param {Object} config The config object
20629  */
20630
20631
20632
20633 Roo.bootstrap.Calendar = function(config){
20634     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20635      this.addEvents({
20636         /**
20637              * @event select
20638              * Fires when a date is selected
20639              * @param {DatePicker} this
20640              * @param {Date} date The selected date
20641              */
20642         'select': true,
20643         /**
20644              * @event monthchange
20645              * Fires when the displayed month changes 
20646              * @param {DatePicker} this
20647              * @param {Date} date The selected month
20648              */
20649         'monthchange': true,
20650         /**
20651              * @event evententer
20652              * Fires when mouse over an event
20653              * @param {Calendar} this
20654              * @param {event} Event
20655              */
20656         'evententer': true,
20657         /**
20658              * @event eventleave
20659              * Fires when the mouse leaves an
20660              * @param {Calendar} this
20661              * @param {event}
20662              */
20663         'eventleave': true,
20664         /**
20665              * @event eventclick
20666              * Fires when the mouse click an
20667              * @param {Calendar} this
20668              * @param {event}
20669              */
20670         'eventclick': true
20671         
20672     });
20673
20674 };
20675
20676 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20677     
20678           /**
20679      * @cfg {Roo.data.Store} store
20680      * The data source for the calendar
20681      */
20682         store : false,
20683      /**
20684      * @cfg {Number} startDay
20685      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20686      */
20687     startDay : 0,
20688     
20689     loadMask : false,
20690     
20691     header : false,
20692       
20693     getAutoCreate : function(){
20694         
20695         
20696         var fc_button = function(name, corner, style, content ) {
20697             return Roo.apply({},{
20698                 tag : 'span',
20699                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20700                          (corner.length ?
20701                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20702                             ''
20703                         ),
20704                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20705                 unselectable: 'on'
20706             });
20707         };
20708         
20709         var header = {};
20710         
20711         if(!this.header){
20712             header = {
20713                 tag : 'table',
20714                 cls : 'fc-header',
20715                 style : 'width:100%',
20716                 cn : [
20717                     {
20718                         tag: 'tr',
20719                         cn : [
20720                             {
20721                                 tag : 'td',
20722                                 cls : 'fc-header-left',
20723                                 cn : [
20724                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20725                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20726                                     { tag: 'span', cls: 'fc-header-space' },
20727                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20728
20729
20730                                 ]
20731                             },
20732
20733                             {
20734                                 tag : 'td',
20735                                 cls : 'fc-header-center',
20736                                 cn : [
20737                                     {
20738                                         tag: 'span',
20739                                         cls: 'fc-header-title',
20740                                         cn : {
20741                                             tag: 'H2',
20742                                             html : 'month / year'
20743                                         }
20744                                     }
20745
20746                                 ]
20747                             },
20748                             {
20749                                 tag : 'td',
20750                                 cls : 'fc-header-right',
20751                                 cn : [
20752                               /*      fc_button('month', 'left', '', 'month' ),
20753                                     fc_button('week', '', '', 'week' ),
20754                                     fc_button('day', 'right', '', 'day' )
20755                                 */    
20756
20757                                 ]
20758                             }
20759
20760                         ]
20761                     }
20762                 ]
20763             };
20764         }
20765         
20766         header = this.header;
20767         
20768        
20769         var cal_heads = function() {
20770             var ret = [];
20771             // fixme - handle this.
20772             
20773             for (var i =0; i < Date.dayNames.length; i++) {
20774                 var d = Date.dayNames[i];
20775                 ret.push({
20776                     tag: 'th',
20777                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20778                     html : d.substring(0,3)
20779                 });
20780                 
20781             }
20782             ret[0].cls += ' fc-first';
20783             ret[6].cls += ' fc-last';
20784             return ret;
20785         };
20786         var cal_cell = function(n) {
20787             return  {
20788                 tag: 'td',
20789                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20790                 cn : [
20791                     {
20792                         cn : [
20793                             {
20794                                 cls: 'fc-day-number',
20795                                 html: 'D'
20796                             },
20797                             {
20798                                 cls: 'fc-day-content',
20799                              
20800                                 cn : [
20801                                      {
20802                                         style: 'position: relative;' // height: 17px;
20803                                     }
20804                                 ]
20805                             }
20806                             
20807                             
20808                         ]
20809                     }
20810                 ]
20811                 
20812             }
20813         };
20814         var cal_rows = function() {
20815             
20816             var ret = [];
20817             for (var r = 0; r < 6; r++) {
20818                 var row= {
20819                     tag : 'tr',
20820                     cls : 'fc-week',
20821                     cn : []
20822                 };
20823                 
20824                 for (var i =0; i < Date.dayNames.length; i++) {
20825                     var d = Date.dayNames[i];
20826                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20827
20828                 }
20829                 row.cn[0].cls+=' fc-first';
20830                 row.cn[0].cn[0].style = 'min-height:90px';
20831                 row.cn[6].cls+=' fc-last';
20832                 ret.push(row);
20833                 
20834             }
20835             ret[0].cls += ' fc-first';
20836             ret[4].cls += ' fc-prev-last';
20837             ret[5].cls += ' fc-last';
20838             return ret;
20839             
20840         };
20841         
20842         var cal_table = {
20843             tag: 'table',
20844             cls: 'fc-border-separate',
20845             style : 'width:100%',
20846             cellspacing  : 0,
20847             cn : [
20848                 { 
20849                     tag: 'thead',
20850                     cn : [
20851                         { 
20852                             tag: 'tr',
20853                             cls : 'fc-first fc-last',
20854                             cn : cal_heads()
20855                         }
20856                     ]
20857                 },
20858                 { 
20859                     tag: 'tbody',
20860                     cn : cal_rows()
20861                 }
20862                   
20863             ]
20864         };
20865          
20866          var cfg = {
20867             cls : 'fc fc-ltr',
20868             cn : [
20869                 header,
20870                 {
20871                     cls : 'fc-content',
20872                     style : "position: relative;",
20873                     cn : [
20874                         {
20875                             cls : 'fc-view fc-view-month fc-grid',
20876                             style : 'position: relative',
20877                             unselectable : 'on',
20878                             cn : [
20879                                 {
20880                                     cls : 'fc-event-container',
20881                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20882                                 },
20883                                 cal_table
20884                             ]
20885                         }
20886                     ]
20887     
20888                 }
20889            ] 
20890             
20891         };
20892         
20893          
20894         
20895         return cfg;
20896     },
20897     
20898     
20899     initEvents : function()
20900     {
20901         if(!this.store){
20902             throw "can not find store for calendar";
20903         }
20904         
20905         var mark = {
20906             tag: "div",
20907             cls:"x-dlg-mask",
20908             style: "text-align:center",
20909             cn: [
20910                 {
20911                     tag: "div",
20912                     style: "background-color:white;width:50%;margin:250 auto",
20913                     cn: [
20914                         {
20915                             tag: "img",
20916                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20917                         },
20918                         {
20919                             tag: "span",
20920                             html: "Loading"
20921                         }
20922                         
20923                     ]
20924                 }
20925             ]
20926         };
20927         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20928         
20929         var size = this.el.select('.fc-content', true).first().getSize();
20930         this.maskEl.setSize(size.width, size.height);
20931         this.maskEl.enableDisplayMode("block");
20932         if(!this.loadMask){
20933             this.maskEl.hide();
20934         }
20935         
20936         this.store = Roo.factory(this.store, Roo.data);
20937         this.store.on('load', this.onLoad, this);
20938         this.store.on('beforeload', this.onBeforeLoad, this);
20939         
20940         this.resize();
20941         
20942         this.cells = this.el.select('.fc-day',true);
20943         //Roo.log(this.cells);
20944         this.textNodes = this.el.query('.fc-day-number');
20945         this.cells.addClassOnOver('fc-state-hover');
20946         
20947         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20948         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20949         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20950         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20951         
20952         this.on('monthchange', this.onMonthChange, this);
20953         
20954         this.update(new Date().clearTime());
20955     },
20956     
20957     resize : function() {
20958         var sz  = this.el.getSize();
20959         
20960         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20961         this.el.select('.fc-day-content div',true).setHeight(34);
20962     },
20963     
20964     
20965     // private
20966     showPrevMonth : function(e){
20967         this.update(this.activeDate.add("mo", -1));
20968     },
20969     showToday : function(e){
20970         this.update(new Date().clearTime());
20971     },
20972     // private
20973     showNextMonth : function(e){
20974         this.update(this.activeDate.add("mo", 1));
20975     },
20976
20977     // private
20978     showPrevYear : function(){
20979         this.update(this.activeDate.add("y", -1));
20980     },
20981
20982     // private
20983     showNextYear : function(){
20984         this.update(this.activeDate.add("y", 1));
20985     },
20986
20987     
20988    // private
20989     update : function(date)
20990     {
20991         var vd = this.activeDate;
20992         this.activeDate = date;
20993 //        if(vd && this.el){
20994 //            var t = date.getTime();
20995 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20996 //                Roo.log('using add remove');
20997 //                
20998 //                this.fireEvent('monthchange', this, date);
20999 //                
21000 //                this.cells.removeClass("fc-state-highlight");
21001 //                this.cells.each(function(c){
21002 //                   if(c.dateValue == t){
21003 //                       c.addClass("fc-state-highlight");
21004 //                       setTimeout(function(){
21005 //                            try{c.dom.firstChild.focus();}catch(e){}
21006 //                       }, 50);
21007 //                       return false;
21008 //                   }
21009 //                   return true;
21010 //                });
21011 //                return;
21012 //            }
21013 //        }
21014         
21015         var days = date.getDaysInMonth();
21016         
21017         var firstOfMonth = date.getFirstDateOfMonth();
21018         var startingPos = firstOfMonth.getDay()-this.startDay;
21019         
21020         if(startingPos < this.startDay){
21021             startingPos += 7;
21022         }
21023         
21024         var pm = date.add(Date.MONTH, -1);
21025         var prevStart = pm.getDaysInMonth()-startingPos;
21026 //        
21027         this.cells = this.el.select('.fc-day',true);
21028         this.textNodes = this.el.query('.fc-day-number');
21029         this.cells.addClassOnOver('fc-state-hover');
21030         
21031         var cells = this.cells.elements;
21032         var textEls = this.textNodes;
21033         
21034         Roo.each(cells, function(cell){
21035             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21036         });
21037         
21038         days += startingPos;
21039
21040         // convert everything to numbers so it's fast
21041         var day = 86400000;
21042         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21043         //Roo.log(d);
21044         //Roo.log(pm);
21045         //Roo.log(prevStart);
21046         
21047         var today = new Date().clearTime().getTime();
21048         var sel = date.clearTime().getTime();
21049         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21050         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21051         var ddMatch = this.disabledDatesRE;
21052         var ddText = this.disabledDatesText;
21053         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21054         var ddaysText = this.disabledDaysText;
21055         var format = this.format;
21056         
21057         var setCellClass = function(cal, cell){
21058             cell.row = 0;
21059             cell.events = [];
21060             cell.more = [];
21061             //Roo.log('set Cell Class');
21062             cell.title = "";
21063             var t = d.getTime();
21064             
21065             //Roo.log(d);
21066             
21067             cell.dateValue = t;
21068             if(t == today){
21069                 cell.className += " fc-today";
21070                 cell.className += " fc-state-highlight";
21071                 cell.title = cal.todayText;
21072             }
21073             if(t == sel){
21074                 // disable highlight in other month..
21075                 //cell.className += " fc-state-highlight";
21076                 
21077             }
21078             // disabling
21079             if(t < min) {
21080                 cell.className = " fc-state-disabled";
21081                 cell.title = cal.minText;
21082                 return;
21083             }
21084             if(t > max) {
21085                 cell.className = " fc-state-disabled";
21086                 cell.title = cal.maxText;
21087                 return;
21088             }
21089             if(ddays){
21090                 if(ddays.indexOf(d.getDay()) != -1){
21091                     cell.title = ddaysText;
21092                     cell.className = " fc-state-disabled";
21093                 }
21094             }
21095             if(ddMatch && format){
21096                 var fvalue = d.dateFormat(format);
21097                 if(ddMatch.test(fvalue)){
21098                     cell.title = ddText.replace("%0", fvalue);
21099                     cell.className = " fc-state-disabled";
21100                 }
21101             }
21102             
21103             if (!cell.initialClassName) {
21104                 cell.initialClassName = cell.dom.className;
21105             }
21106             
21107             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21108         };
21109
21110         var i = 0;
21111         
21112         for(; i < startingPos; i++) {
21113             textEls[i].innerHTML = (++prevStart);
21114             d.setDate(d.getDate()+1);
21115             
21116             cells[i].className = "fc-past fc-other-month";
21117             setCellClass(this, cells[i]);
21118         }
21119         
21120         var intDay = 0;
21121         
21122         for(; i < days; i++){
21123             intDay = i - startingPos + 1;
21124             textEls[i].innerHTML = (intDay);
21125             d.setDate(d.getDate()+1);
21126             
21127             cells[i].className = ''; // "x-date-active";
21128             setCellClass(this, cells[i]);
21129         }
21130         var extraDays = 0;
21131         
21132         for(; i < 42; i++) {
21133             textEls[i].innerHTML = (++extraDays);
21134             d.setDate(d.getDate()+1);
21135             
21136             cells[i].className = "fc-future fc-other-month";
21137             setCellClass(this, cells[i]);
21138         }
21139         
21140         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21141         
21142         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21143         
21144         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21145         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21146         
21147         if(totalRows != 6){
21148             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21149             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21150         }
21151         
21152         this.fireEvent('monthchange', this, date);
21153         
21154         
21155         /*
21156         if(!this.internalRender){
21157             var main = this.el.dom.firstChild;
21158             var w = main.offsetWidth;
21159             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21160             Roo.fly(main).setWidth(w);
21161             this.internalRender = true;
21162             // opera does not respect the auto grow header center column
21163             // then, after it gets a width opera refuses to recalculate
21164             // without a second pass
21165             if(Roo.isOpera && !this.secondPass){
21166                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21167                 this.secondPass = true;
21168                 this.update.defer(10, this, [date]);
21169             }
21170         }
21171         */
21172         
21173     },
21174     
21175     findCell : function(dt) {
21176         dt = dt.clearTime().getTime();
21177         var ret = false;
21178         this.cells.each(function(c){
21179             //Roo.log("check " +c.dateValue + '?=' + dt);
21180             if(c.dateValue == dt){
21181                 ret = c;
21182                 return false;
21183             }
21184             return true;
21185         });
21186         
21187         return ret;
21188     },
21189     
21190     findCells : function(ev) {
21191         var s = ev.start.clone().clearTime().getTime();
21192        // Roo.log(s);
21193         var e= ev.end.clone().clearTime().getTime();
21194        // Roo.log(e);
21195         var ret = [];
21196         this.cells.each(function(c){
21197              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21198             
21199             if(c.dateValue > e){
21200                 return ;
21201             }
21202             if(c.dateValue < s){
21203                 return ;
21204             }
21205             ret.push(c);
21206         });
21207         
21208         return ret;    
21209     },
21210     
21211 //    findBestRow: function(cells)
21212 //    {
21213 //        var ret = 0;
21214 //        
21215 //        for (var i =0 ; i < cells.length;i++) {
21216 //            ret  = Math.max(cells[i].rows || 0,ret);
21217 //        }
21218 //        return ret;
21219 //        
21220 //    },
21221     
21222     
21223     addItem : function(ev)
21224     {
21225         // look for vertical location slot in
21226         var cells = this.findCells(ev);
21227         
21228 //        ev.row = this.findBestRow(cells);
21229         
21230         // work out the location.
21231         
21232         var crow = false;
21233         var rows = [];
21234         for(var i =0; i < cells.length; i++) {
21235             
21236             cells[i].row = cells[0].row;
21237             
21238             if(i == 0){
21239                 cells[i].row = cells[i].row + 1;
21240             }
21241             
21242             if (!crow) {
21243                 crow = {
21244                     start : cells[i],
21245                     end :  cells[i]
21246                 };
21247                 continue;
21248             }
21249             if (crow.start.getY() == cells[i].getY()) {
21250                 // on same row.
21251                 crow.end = cells[i];
21252                 continue;
21253             }
21254             // different row.
21255             rows.push(crow);
21256             crow = {
21257                 start: cells[i],
21258                 end : cells[i]
21259             };
21260             
21261         }
21262         
21263         rows.push(crow);
21264         ev.els = [];
21265         ev.rows = rows;
21266         ev.cells = cells;
21267         
21268         cells[0].events.push(ev);
21269         
21270         this.calevents.push(ev);
21271     },
21272     
21273     clearEvents: function() {
21274         
21275         if(!this.calevents){
21276             return;
21277         }
21278         
21279         Roo.each(this.cells.elements, function(c){
21280             c.row = 0;
21281             c.events = [];
21282             c.more = [];
21283         });
21284         
21285         Roo.each(this.calevents, function(e) {
21286             Roo.each(e.els, function(el) {
21287                 el.un('mouseenter' ,this.onEventEnter, this);
21288                 el.un('mouseleave' ,this.onEventLeave, this);
21289                 el.remove();
21290             },this);
21291         },this);
21292         
21293         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21294             e.remove();
21295         });
21296         
21297     },
21298     
21299     renderEvents: function()
21300     {   
21301         var _this = this;
21302         
21303         this.cells.each(function(c) {
21304             
21305             if(c.row < 5){
21306                 return;
21307             }
21308             
21309             var ev = c.events;
21310             
21311             var r = 4;
21312             if(c.row != c.events.length){
21313                 r = 4 - (4 - (c.row - c.events.length));
21314             }
21315             
21316             c.events = ev.slice(0, r);
21317             c.more = ev.slice(r);
21318             
21319             if(c.more.length && c.more.length == 1){
21320                 c.events.push(c.more.pop());
21321             }
21322             
21323             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21324             
21325         });
21326             
21327         this.cells.each(function(c) {
21328             
21329             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21330             
21331             
21332             for (var e = 0; e < c.events.length; e++){
21333                 var ev = c.events[e];
21334                 var rows = ev.rows;
21335                 
21336                 for(var i = 0; i < rows.length; i++) {
21337                 
21338                     // how many rows should it span..
21339
21340                     var  cfg = {
21341                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21342                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21343
21344                         unselectable : "on",
21345                         cn : [
21346                             {
21347                                 cls: 'fc-event-inner',
21348                                 cn : [
21349     //                                {
21350     //                                  tag:'span',
21351     //                                  cls: 'fc-event-time',
21352     //                                  html : cells.length > 1 ? '' : ev.time
21353     //                                },
21354                                     {
21355                                       tag:'span',
21356                                       cls: 'fc-event-title',
21357                                       html : String.format('{0}', ev.title)
21358                                     }
21359
21360
21361                                 ]
21362                             },
21363                             {
21364                                 cls: 'ui-resizable-handle ui-resizable-e',
21365                                 html : '&nbsp;&nbsp;&nbsp'
21366                             }
21367
21368                         ]
21369                     };
21370
21371                     if (i == 0) {
21372                         cfg.cls += ' fc-event-start';
21373                     }
21374                     if ((i+1) == rows.length) {
21375                         cfg.cls += ' fc-event-end';
21376                     }
21377
21378                     var ctr = _this.el.select('.fc-event-container',true).first();
21379                     var cg = ctr.createChild(cfg);
21380
21381                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21382                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21383
21384                     var r = (c.more.length) ? 1 : 0;
21385                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21386                     cg.setWidth(ebox.right - sbox.x -2);
21387
21388                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21389                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21390                     cg.on('click', _this.onEventClick, _this, ev);
21391
21392                     ev.els.push(cg);
21393                     
21394                 }
21395                 
21396             }
21397             
21398             
21399             if(c.more.length){
21400                 var  cfg = {
21401                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21402                     style : 'position: absolute',
21403                     unselectable : "on",
21404                     cn : [
21405                         {
21406                             cls: 'fc-event-inner',
21407                             cn : [
21408                                 {
21409                                   tag:'span',
21410                                   cls: 'fc-event-title',
21411                                   html : 'More'
21412                                 }
21413
21414
21415                             ]
21416                         },
21417                         {
21418                             cls: 'ui-resizable-handle ui-resizable-e',
21419                             html : '&nbsp;&nbsp;&nbsp'
21420                         }
21421
21422                     ]
21423                 };
21424
21425                 var ctr = _this.el.select('.fc-event-container',true).first();
21426                 var cg = ctr.createChild(cfg);
21427
21428                 var sbox = c.select('.fc-day-content',true).first().getBox();
21429                 var ebox = c.select('.fc-day-content',true).first().getBox();
21430                 //Roo.log(cg);
21431                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21432                 cg.setWidth(ebox.right - sbox.x -2);
21433
21434                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21435                 
21436             }
21437             
21438         });
21439         
21440         
21441         
21442     },
21443     
21444     onEventEnter: function (e, el,event,d) {
21445         this.fireEvent('evententer', this, el, event);
21446     },
21447     
21448     onEventLeave: function (e, el,event,d) {
21449         this.fireEvent('eventleave', this, el, event);
21450     },
21451     
21452     onEventClick: function (e, el,event,d) {
21453         this.fireEvent('eventclick', this, el, event);
21454     },
21455     
21456     onMonthChange: function () {
21457         this.store.load();
21458     },
21459     
21460     onMoreEventClick: function(e, el, more)
21461     {
21462         var _this = this;
21463         
21464         this.calpopover.placement = 'right';
21465         this.calpopover.setTitle('More');
21466         
21467         this.calpopover.setContent('');
21468         
21469         var ctr = this.calpopover.el.select('.popover-content', true).first();
21470         
21471         Roo.each(more, function(m){
21472             var cfg = {
21473                 cls : 'fc-event-hori fc-event-draggable',
21474                 html : m.title
21475             };
21476             var cg = ctr.createChild(cfg);
21477             
21478             cg.on('click', _this.onEventClick, _this, m);
21479         });
21480         
21481         this.calpopover.show(el);
21482         
21483         
21484     },
21485     
21486     onLoad: function () 
21487     {   
21488         this.calevents = [];
21489         var cal = this;
21490         
21491         if(this.store.getCount() > 0){
21492             this.store.data.each(function(d){
21493                cal.addItem({
21494                     id : d.data.id,
21495                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21496                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21497                     time : d.data.start_time,
21498                     title : d.data.title,
21499                     description : d.data.description,
21500                     venue : d.data.venue
21501                 });
21502             });
21503         }
21504         
21505         this.renderEvents();
21506         
21507         if(this.calevents.length && this.loadMask){
21508             this.maskEl.hide();
21509         }
21510     },
21511     
21512     onBeforeLoad: function()
21513     {
21514         this.clearEvents();
21515         if(this.loadMask){
21516             this.maskEl.show();
21517         }
21518     }
21519 });
21520
21521  
21522  /*
21523  * - LGPL
21524  *
21525  * element
21526  * 
21527  */
21528
21529 /**
21530  * @class Roo.bootstrap.Popover
21531  * @extends Roo.bootstrap.Component
21532  * @parent none builder
21533  * @children Roo.bootstrap.Component
21534  * Bootstrap Popover class
21535  * @cfg {String} html contents of the popover   (or false to use children..)
21536  * @cfg {String} title of popover (or false to hide)
21537  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21538  * @cfg {String} trigger click || hover (or false to trigger manually)
21539  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21540  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21541  *      - if false and it has a 'parent' then it will be automatically added to that element
21542  *      - if string - Roo.get  will be called 
21543  * @cfg {Number} delay - delay before showing
21544  
21545  * @constructor
21546  * Create a new Popover
21547  * @param {Object} config The config object
21548  */
21549
21550 Roo.bootstrap.Popover = function(config){
21551     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21552     
21553     this.addEvents({
21554         // raw events
21555          /**
21556          * @event show
21557          * After the popover show
21558          * 
21559          * @param {Roo.bootstrap.Popover} this
21560          */
21561         "show" : true,
21562         /**
21563          * @event hide
21564          * After the popover hide
21565          * 
21566          * @param {Roo.bootstrap.Popover} this
21567          */
21568         "hide" : true
21569     });
21570 };
21571
21572 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21573     
21574     title: false,
21575     html: false,
21576     
21577     placement : 'right',
21578     trigger : 'hover', // hover
21579     modal : false,
21580     delay : 0,
21581     
21582     over: false,
21583     
21584     can_build_overlaid : false,
21585     
21586     maskEl : false, // the mask element
21587     headerEl : false,
21588     contentEl : false,
21589     alignEl : false, // when show is called with an element - this get's stored.
21590     
21591     getChildContainer : function()
21592     {
21593         return this.contentEl;
21594         
21595     },
21596     getPopoverHeader : function()
21597     {
21598         this.title = true; // flag not to hide it..
21599         this.headerEl.addClass('p-0');
21600         return this.headerEl
21601     },
21602     
21603     
21604     getAutoCreate : function(){
21605          
21606         var cfg = {
21607            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21608            style: 'display:block',
21609            cn : [
21610                 {
21611                     cls : 'arrow'
21612                 },
21613                 {
21614                     cls : 'popover-inner ',
21615                     cn : [
21616                         {
21617                             tag: 'h3',
21618                             cls: 'popover-title popover-header',
21619                             html : this.title === false ? '' : this.title
21620                         },
21621                         {
21622                             cls : 'popover-content popover-body '  + (this.cls || ''),
21623                             html : this.html || ''
21624                         }
21625                     ]
21626                     
21627                 }
21628            ]
21629         };
21630         
21631         return cfg;
21632     },
21633     /**
21634      * @param {string} the title
21635      */
21636     setTitle: function(str)
21637     {
21638         this.title = str;
21639         if (this.el) {
21640             this.headerEl.dom.innerHTML = str;
21641         }
21642         
21643     },
21644     /**
21645      * @param {string} the body content
21646      */
21647     setContent: function(str)
21648     {
21649         this.html = str;
21650         if (this.contentEl) {
21651             this.contentEl.dom.innerHTML = str;
21652         }
21653         
21654     },
21655     // as it get's added to the bottom of the page.
21656     onRender : function(ct, position)
21657     {
21658         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21659         
21660         
21661         
21662         if(!this.el){
21663             var cfg = Roo.apply({},  this.getAutoCreate());
21664             cfg.id = Roo.id();
21665             
21666             if (this.cls) {
21667                 cfg.cls += ' ' + this.cls;
21668             }
21669             if (this.style) {
21670                 cfg.style = this.style;
21671             }
21672             //Roo.log("adding to ");
21673             this.el = Roo.get(document.body).createChild(cfg, position);
21674 //            Roo.log(this.el);
21675         }
21676         
21677         this.contentEl = this.el.select('.popover-content',true).first();
21678         this.headerEl =  this.el.select('.popover-title',true).first();
21679         
21680         var nitems = [];
21681         if(typeof(this.items) != 'undefined'){
21682             var items = this.items;
21683             delete this.items;
21684
21685             for(var i =0;i < items.length;i++) {
21686                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21687             }
21688         }
21689
21690         this.items = nitems;
21691         
21692         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21693         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21694         
21695         
21696         
21697         this.initEvents();
21698     },
21699     
21700     resizeMask : function()
21701     {
21702         this.maskEl.setSize(
21703             Roo.lib.Dom.getViewWidth(true),
21704             Roo.lib.Dom.getViewHeight(true)
21705         );
21706     },
21707     
21708     initEvents : function()
21709     {
21710         
21711         if (!this.modal) { 
21712             Roo.bootstrap.Popover.register(this);
21713         }
21714          
21715         this.arrowEl = this.el.select('.arrow',true).first();
21716         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21717         this.el.enableDisplayMode('block');
21718         this.el.hide();
21719  
21720         
21721         if (this.over === false && !this.parent()) {
21722             return; 
21723         }
21724         if (this.triggers === false) {
21725             return;
21726         }
21727          
21728         // support parent
21729         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21730         var triggers = this.trigger ? this.trigger.split(' ') : [];
21731         Roo.each(triggers, function(trigger) {
21732         
21733             if (trigger == 'click') {
21734                 on_el.on('click', this.toggle, this);
21735             } else if (trigger != 'manual') {
21736                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21737                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21738       
21739                 on_el.on(eventIn  ,this.enter, this);
21740                 on_el.on(eventOut, this.leave, this);
21741             }
21742         }, this);
21743     },
21744     
21745     
21746     // private
21747     timeout : null,
21748     hoverState : null,
21749     
21750     toggle : function () {
21751         this.hoverState == 'in' ? this.leave() : this.enter();
21752     },
21753     
21754     enter : function () {
21755         
21756         clearTimeout(this.timeout);
21757     
21758         this.hoverState = 'in';
21759     
21760         if (!this.delay || !this.delay.show) {
21761             this.show();
21762             return;
21763         }
21764         var _t = this;
21765         this.timeout = setTimeout(function () {
21766             if (_t.hoverState == 'in') {
21767                 _t.show();
21768             }
21769         }, this.delay.show)
21770     },
21771     
21772     leave : function() {
21773         clearTimeout(this.timeout);
21774     
21775         this.hoverState = 'out';
21776     
21777         if (!this.delay || !this.delay.hide) {
21778             this.hide();
21779             return;
21780         }
21781         var _t = this;
21782         this.timeout = setTimeout(function () {
21783             if (_t.hoverState == 'out') {
21784                 _t.hide();
21785             }
21786         }, this.delay.hide)
21787     },
21788     
21789     /**
21790      * update the position of the dialog
21791      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21792      * 
21793      *
21794      */
21795     
21796     doAlign : function()
21797     {
21798         
21799         if (this.alignEl) {
21800             this.updatePosition(this.placement, true);
21801              
21802         } else {
21803             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21804             var es = this.el.getSize();
21805             var x = Roo.lib.Dom.getViewWidth()/2;
21806             var y = Roo.lib.Dom.getViewHeight()/2;
21807             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21808             
21809         }
21810
21811          
21812          
21813         
21814         
21815     },
21816     
21817     /**
21818      * Show the popover
21819      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21820      * @param {string} (left|right|top|bottom) position
21821      */
21822     show : function (on_el, placement)
21823     {
21824         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21825         on_el = on_el || false; // default to false
21826          
21827         if (!on_el) {
21828             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21829                 on_el = this.parent().el;
21830             } else if (this.over) {
21831                 on_el = Roo.get(this.over);
21832             }
21833             
21834         }
21835         
21836         this.alignEl = Roo.get( on_el );
21837
21838         if (!this.el) {
21839             this.render(document.body);
21840         }
21841         
21842         
21843          
21844         
21845         if (this.title === false) {
21846             this.headerEl.hide();
21847         }
21848         
21849        
21850         this.el.show();
21851         this.el.dom.style.display = 'block';
21852          
21853         this.doAlign();
21854         
21855         //var arrow = this.el.select('.arrow',true).first();
21856         //arrow.set(align[2], 
21857         
21858         this.el.addClass('in');
21859         
21860          
21861         
21862         this.hoverState = 'in';
21863         
21864         if (this.modal) {
21865             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21866             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21867             this.maskEl.dom.style.display = 'block';
21868             this.maskEl.addClass('show');
21869         }
21870         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21871  
21872         this.fireEvent('show', this);
21873         
21874     },
21875     /**
21876      * fire this manually after loading a grid in the table for example
21877      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21878      * @param {Boolean} try and move it if we cant get right position.
21879      */
21880     updatePosition : function(placement, try_move)
21881     {
21882         // allow for calling with no parameters
21883         placement = placement   ? placement :  this.placement;
21884         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21885         
21886         this.el.removeClass([
21887             'fade','top','bottom', 'left', 'right','in',
21888             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21889         ]);
21890         this.el.addClass(placement + ' bs-popover-' + placement);
21891         
21892         if (!this.alignEl ) {
21893             return false;
21894         }
21895         
21896         switch (placement) {
21897             case 'right':
21898                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21899                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21900                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21901                     //normal display... or moved up/down.
21902                     this.el.setXY(offset);
21903                     var xy = this.alignEl.getAnchorXY('tr', false);
21904                     xy[0]+=2;xy[1]+=5;
21905                     this.arrowEl.setXY(xy);
21906                     return true;
21907                 }
21908                 // continue through...
21909                 return this.updatePosition('left', false);
21910                 
21911             
21912             case 'left':
21913                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21914                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21915                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21916                     //normal display... or moved up/down.
21917                     this.el.setXY(offset);
21918                     var xy = this.alignEl.getAnchorXY('tl', false);
21919                     xy[0]-=10;xy[1]+=5; // << fix me
21920                     this.arrowEl.setXY(xy);
21921                     return true;
21922                 }
21923                 // call self...
21924                 return this.updatePosition('right', false);
21925             
21926             case 'top':
21927                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21928                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21929                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21930                     //normal display... or moved up/down.
21931                     this.el.setXY(offset);
21932                     var xy = this.alignEl.getAnchorXY('t', false);
21933                     xy[1]-=10; // << fix me
21934                     this.arrowEl.setXY(xy);
21935                     return true;
21936                 }
21937                 // fall through
21938                return this.updatePosition('bottom', false);
21939             
21940             case 'bottom':
21941                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21942                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21943                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21944                     //normal display... or moved up/down.
21945                     this.el.setXY(offset);
21946                     var xy = this.alignEl.getAnchorXY('b', false);
21947                      xy[1]+=2; // << fix me
21948                     this.arrowEl.setXY(xy);
21949                     return true;
21950                 }
21951                 // fall through
21952                 return this.updatePosition('top', false);
21953                 
21954             
21955         }
21956         
21957         
21958         return false;
21959     },
21960     
21961     hide : function()
21962     {
21963         this.el.setXY([0,0]);
21964         this.el.removeClass('in');
21965         this.el.hide();
21966         this.hoverState = null;
21967         this.maskEl.hide(); // always..
21968         this.fireEvent('hide', this);
21969     }
21970     
21971 });
21972
21973
21974 Roo.apply(Roo.bootstrap.Popover, {
21975
21976     alignment : {
21977         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21978         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21979         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21980         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21981     },
21982     
21983     zIndex : 20001,
21984
21985     clickHander : false,
21986     
21987     
21988
21989     onMouseDown : function(e)
21990     {
21991         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21992             /// what is nothing is showing..
21993             this.hideAll();
21994         }
21995          
21996     },
21997     
21998     
21999     popups : [],
22000     
22001     register : function(popup)
22002     {
22003         if (!Roo.bootstrap.Popover.clickHandler) {
22004             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22005         }
22006         // hide other popups.
22007         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22008         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22009         this.hideAll(); //<< why?
22010         //this.popups.push(popup);
22011     },
22012     hideAll : function()
22013     {
22014         this.popups.forEach(function(p) {
22015             p.hide();
22016         });
22017     },
22018     onShow : function() {
22019         Roo.bootstrap.Popover.popups.push(this);
22020     },
22021     onHide : function() {
22022         Roo.bootstrap.Popover.popups.remove(this);
22023     } 
22024
22025 });
22026 /**
22027  * @class Roo.bootstrap.PopoverNav
22028  * @extends Roo.bootstrap.nav.Simplebar
22029  * @parent Roo.bootstrap.Popover
22030  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22031  * @licence LGPL
22032  * Bootstrap Popover header navigation class
22033  * FIXME? should this go under nav?
22034  *
22035  * 
22036  * @constructor
22037  * Create a new Popover Header Navigation 
22038  * @param {Object} config The config object
22039  */
22040
22041 Roo.bootstrap.PopoverNav = function(config){
22042     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22043 };
22044
22045 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22046     
22047     
22048     container_method : 'getPopoverHeader' 
22049     
22050      
22051     
22052     
22053    
22054 });
22055
22056  
22057
22058  /*
22059  * - LGPL
22060  *
22061  * Progress
22062  * 
22063  */
22064
22065 /**
22066  * @class Roo.bootstrap.Progress
22067  * @extends Roo.bootstrap.Component
22068  * @children Roo.bootstrap.ProgressBar
22069  * Bootstrap Progress class
22070  * @cfg {Boolean} striped striped of the progress bar
22071  * @cfg {Boolean} active animated of the progress bar
22072  * 
22073  * 
22074  * @constructor
22075  * Create a new Progress
22076  * @param {Object} config The config object
22077  */
22078
22079 Roo.bootstrap.Progress = function(config){
22080     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22081 };
22082
22083 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22084     
22085     striped : false,
22086     active: false,
22087     
22088     getAutoCreate : function(){
22089         var cfg = {
22090             tag: 'div',
22091             cls: 'progress'
22092         };
22093         
22094         
22095         if(this.striped){
22096             cfg.cls += ' progress-striped';
22097         }
22098       
22099         if(this.active){
22100             cfg.cls += ' active';
22101         }
22102         
22103         
22104         return cfg;
22105     }
22106    
22107 });
22108
22109  
22110
22111  /*
22112  * - LGPL
22113  *
22114  * ProgressBar
22115  * 
22116  */
22117
22118 /**
22119  * @class Roo.bootstrap.ProgressBar
22120  * @extends Roo.bootstrap.Component
22121  * Bootstrap ProgressBar class
22122  * @cfg {Number} aria_valuenow aria-value now
22123  * @cfg {Number} aria_valuemin aria-value min
22124  * @cfg {Number} aria_valuemax aria-value max
22125  * @cfg {String} label label for the progress bar
22126  * @cfg {String} panel (success | info | warning | danger )
22127  * @cfg {String} role role of the progress bar
22128  * @cfg {String} sr_only text
22129  * 
22130  * 
22131  * @constructor
22132  * Create a new ProgressBar
22133  * @param {Object} config The config object
22134  */
22135
22136 Roo.bootstrap.ProgressBar = function(config){
22137     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22138 };
22139
22140 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22141     
22142     aria_valuenow : 0,
22143     aria_valuemin : 0,
22144     aria_valuemax : 100,
22145     label : false,
22146     panel : false,
22147     role : false,
22148     sr_only: false,
22149     
22150     getAutoCreate : function()
22151     {
22152         
22153         var cfg = {
22154             tag: 'div',
22155             cls: 'progress-bar',
22156             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22157         };
22158         
22159         if(this.sr_only){
22160             cfg.cn = {
22161                 tag: 'span',
22162                 cls: 'sr-only',
22163                 html: this.sr_only
22164             }
22165         }
22166         
22167         if(this.role){
22168             cfg.role = this.role;
22169         }
22170         
22171         if(this.aria_valuenow){
22172             cfg['aria-valuenow'] = this.aria_valuenow;
22173         }
22174         
22175         if(this.aria_valuemin){
22176             cfg['aria-valuemin'] = this.aria_valuemin;
22177         }
22178         
22179         if(this.aria_valuemax){
22180             cfg['aria-valuemax'] = this.aria_valuemax;
22181         }
22182         
22183         if(this.label && !this.sr_only){
22184             cfg.html = this.label;
22185         }
22186         
22187         if(this.panel){
22188             cfg.cls += ' progress-bar-' + this.panel;
22189         }
22190         
22191         return cfg;
22192     },
22193     
22194     update : function(aria_valuenow)
22195     {
22196         this.aria_valuenow = aria_valuenow;
22197         
22198         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22199     }
22200    
22201 });
22202
22203  
22204
22205  /**
22206  * @class Roo.bootstrap.TabGroup
22207  * @extends Roo.bootstrap.Column
22208  * @children Roo.bootstrap.TabPanel
22209  * Bootstrap Column class
22210  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22211  * @cfg {Boolean} carousel true to make the group behave like a carousel
22212  * @cfg {Boolean} bullets show bullets for the panels
22213  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22214  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22215  * @cfg {Boolean} showarrow (true|false) show arrow default true
22216  * 
22217  * @constructor
22218  * Create a new TabGroup
22219  * @param {Object} config The config object
22220  */
22221
22222 Roo.bootstrap.TabGroup = function(config){
22223     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22224     if (!this.navId) {
22225         this.navId = Roo.id();
22226     }
22227     this.tabs = [];
22228     Roo.bootstrap.TabGroup.register(this);
22229     
22230 };
22231
22232 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22233     
22234     carousel : false,
22235     transition : false,
22236     bullets : 0,
22237     timer : 0,
22238     autoslide : false,
22239     slideFn : false,
22240     slideOnTouch : false,
22241     showarrow : true,
22242     
22243     getAutoCreate : function()
22244     {
22245         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22246         
22247         cfg.cls += ' tab-content';
22248         
22249         if (this.carousel) {
22250             cfg.cls += ' carousel slide';
22251             
22252             cfg.cn = [{
22253                cls : 'carousel-inner',
22254                cn : []
22255             }];
22256         
22257             if(this.bullets  && !Roo.isTouch){
22258                 
22259                 var bullets = {
22260                     cls : 'carousel-bullets',
22261                     cn : []
22262                 };
22263                
22264                 if(this.bullets_cls){
22265                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22266                 }
22267                 
22268                 bullets.cn.push({
22269                     cls : 'clear'
22270                 });
22271                 
22272                 cfg.cn[0].cn.push(bullets);
22273             }
22274             
22275             if(this.showarrow){
22276                 cfg.cn[0].cn.push({
22277                     tag : 'div',
22278                     class : 'carousel-arrow',
22279                     cn : [
22280                         {
22281                             tag : 'div',
22282                             class : 'carousel-prev',
22283                             cn : [
22284                                 {
22285                                     tag : 'i',
22286                                     class : 'fa fa-chevron-left'
22287                                 }
22288                             ]
22289                         },
22290                         {
22291                             tag : 'div',
22292                             class : 'carousel-next',
22293                             cn : [
22294                                 {
22295                                     tag : 'i',
22296                                     class : 'fa fa-chevron-right'
22297                                 }
22298                             ]
22299                         }
22300                     ]
22301                 });
22302             }
22303             
22304         }
22305         
22306         return cfg;
22307     },
22308     
22309     initEvents:  function()
22310     {
22311 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22312 //            this.el.on("touchstart", this.onTouchStart, this);
22313 //        }
22314         
22315         if(this.autoslide){
22316             var _this = this;
22317             
22318             this.slideFn = window.setInterval(function() {
22319                 _this.showPanelNext();
22320             }, this.timer);
22321         }
22322         
22323         if(this.showarrow){
22324             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22325             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22326         }
22327         
22328         
22329     },
22330     
22331 //    onTouchStart : function(e, el, o)
22332 //    {
22333 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22334 //            return;
22335 //        }
22336 //        
22337 //        this.showPanelNext();
22338 //    },
22339     
22340     
22341     getChildContainer : function()
22342     {
22343         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22344     },
22345     
22346     /**
22347     * register a Navigation item
22348     * @param {Roo.bootstrap.nav.Item} the navitem to add
22349     */
22350     register : function(item)
22351     {
22352         this.tabs.push( item);
22353         item.navId = this.navId; // not really needed..
22354         this.addBullet();
22355     
22356     },
22357     
22358     getActivePanel : function()
22359     {
22360         var r = false;
22361         Roo.each(this.tabs, function(t) {
22362             if (t.active) {
22363                 r = t;
22364                 return false;
22365             }
22366             return null;
22367         });
22368         return r;
22369         
22370     },
22371     getPanelByName : function(n)
22372     {
22373         var r = false;
22374         Roo.each(this.tabs, function(t) {
22375             if (t.tabId == n) {
22376                 r = t;
22377                 return false;
22378             }
22379             return null;
22380         });
22381         return r;
22382     },
22383     indexOfPanel : function(p)
22384     {
22385         var r = false;
22386         Roo.each(this.tabs, function(t,i) {
22387             if (t.tabId == p.tabId) {
22388                 r = i;
22389                 return false;
22390             }
22391             return null;
22392         });
22393         return r;
22394     },
22395     /**
22396      * show a specific panel
22397      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22398      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22399      */
22400     showPanel : function (pan)
22401     {
22402         if(this.transition || typeof(pan) == 'undefined'){
22403             Roo.log("waiting for the transitionend");
22404             return false;
22405         }
22406         
22407         if (typeof(pan) == 'number') {
22408             pan = this.tabs[pan];
22409         }
22410         
22411         if (typeof(pan) == 'string') {
22412             pan = this.getPanelByName(pan);
22413         }
22414         
22415         var cur = this.getActivePanel();
22416         
22417         if(!pan || !cur){
22418             Roo.log('pan or acitve pan is undefined');
22419             return false;
22420         }
22421         
22422         if (pan.tabId == this.getActivePanel().tabId) {
22423             return true;
22424         }
22425         
22426         if (false === cur.fireEvent('beforedeactivate')) {
22427             return false;
22428         }
22429         
22430         if(this.bullets > 0 && !Roo.isTouch){
22431             this.setActiveBullet(this.indexOfPanel(pan));
22432         }
22433         
22434         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22435             
22436             //class="carousel-item carousel-item-next carousel-item-left"
22437             
22438             this.transition = true;
22439             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22440             var lr = dir == 'next' ? 'left' : 'right';
22441             pan.el.addClass(dir); // or prev
22442             pan.el.addClass('carousel-item-' + dir); // or prev
22443             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22444             cur.el.addClass(lr); // or right
22445             pan.el.addClass(lr);
22446             cur.el.addClass('carousel-item-' +lr); // or right
22447             pan.el.addClass('carousel-item-' +lr);
22448             
22449             
22450             var _this = this;
22451             cur.el.on('transitionend', function() {
22452                 Roo.log("trans end?");
22453                 
22454                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22455                 pan.setActive(true);
22456                 
22457                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22458                 cur.setActive(false);
22459                 
22460                 _this.transition = false;
22461                 
22462             }, this, { single:  true } );
22463             
22464             return true;
22465         }
22466         
22467         cur.setActive(false);
22468         pan.setActive(true);
22469         
22470         return true;
22471         
22472     },
22473     showPanelNext : function()
22474     {
22475         var i = this.indexOfPanel(this.getActivePanel());
22476         
22477         if (i >= this.tabs.length - 1 && !this.autoslide) {
22478             return;
22479         }
22480         
22481         if (i >= this.tabs.length - 1 && this.autoslide) {
22482             i = -1;
22483         }
22484         
22485         this.showPanel(this.tabs[i+1]);
22486     },
22487     
22488     showPanelPrev : function()
22489     {
22490         var i = this.indexOfPanel(this.getActivePanel());
22491         
22492         if (i  < 1 && !this.autoslide) {
22493             return;
22494         }
22495         
22496         if (i < 1 && this.autoslide) {
22497             i = this.tabs.length;
22498         }
22499         
22500         this.showPanel(this.tabs[i-1]);
22501     },
22502     
22503     
22504     addBullet: function()
22505     {
22506         if(!this.bullets || Roo.isTouch){
22507             return;
22508         }
22509         var ctr = this.el.select('.carousel-bullets',true).first();
22510         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22511         var bullet = ctr.createChild({
22512             cls : 'bullet bullet-' + i
22513         },ctr.dom.lastChild);
22514         
22515         
22516         var _this = this;
22517         
22518         bullet.on('click', (function(e, el, o, ii, t){
22519
22520             e.preventDefault();
22521
22522             this.showPanel(ii);
22523
22524             if(this.autoslide && this.slideFn){
22525                 clearInterval(this.slideFn);
22526                 this.slideFn = window.setInterval(function() {
22527                     _this.showPanelNext();
22528                 }, this.timer);
22529             }
22530
22531         }).createDelegate(this, [i, bullet], true));
22532                 
22533         
22534     },
22535      
22536     setActiveBullet : function(i)
22537     {
22538         if(Roo.isTouch){
22539             return;
22540         }
22541         
22542         Roo.each(this.el.select('.bullet', true).elements, function(el){
22543             el.removeClass('selected');
22544         });
22545
22546         var bullet = this.el.select('.bullet-' + i, true).first();
22547         
22548         if(!bullet){
22549             return;
22550         }
22551         
22552         bullet.addClass('selected');
22553     }
22554     
22555     
22556   
22557 });
22558
22559  
22560
22561  
22562  
22563 Roo.apply(Roo.bootstrap.TabGroup, {
22564     
22565     groups: {},
22566      /**
22567     * register a Navigation Group
22568     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22569     */
22570     register : function(navgrp)
22571     {
22572         this.groups[navgrp.navId] = navgrp;
22573         
22574     },
22575     /**
22576     * fetch a Navigation Group based on the navigation ID
22577     * if one does not exist , it will get created.
22578     * @param {string} the navgroup to add
22579     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22580     */
22581     get: function(navId) {
22582         if (typeof(this.groups[navId]) == 'undefined') {
22583             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22584         }
22585         return this.groups[navId] ;
22586     }
22587     
22588     
22589     
22590 });
22591
22592  /*
22593  * - LGPL
22594  *
22595  * TabPanel
22596  * 
22597  */
22598
22599 /**
22600  * @class Roo.bootstrap.TabPanel
22601  * @extends Roo.bootstrap.Component
22602  * @children Roo.bootstrap.Component
22603  * Bootstrap TabPanel class
22604  * @cfg {Boolean} active panel active
22605  * @cfg {String} html panel content
22606  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22607  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22608  * @cfg {String} href click to link..
22609  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22610  * 
22611  * 
22612  * @constructor
22613  * Create a new TabPanel
22614  * @param {Object} config The config object
22615  */
22616
22617 Roo.bootstrap.TabPanel = function(config){
22618     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22619     this.addEvents({
22620         /**
22621              * @event changed
22622              * Fires when the active status changes
22623              * @param {Roo.bootstrap.TabPanel} this
22624              * @param {Boolean} state the new state
22625             
22626          */
22627         'changed': true,
22628         /**
22629              * @event beforedeactivate
22630              * Fires before a tab is de-activated - can be used to do validation on a form.
22631              * @param {Roo.bootstrap.TabPanel} this
22632              * @return {Boolean} false if there is an error
22633             
22634          */
22635         'beforedeactivate': true
22636      });
22637     
22638     this.tabId = this.tabId || Roo.id();
22639   
22640 };
22641
22642 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22643     
22644     active: false,
22645     html: false,
22646     tabId: false,
22647     navId : false,
22648     href : '',
22649     touchSlide : false,
22650     getAutoCreate : function(){
22651         
22652         
22653         var cfg = {
22654             tag: 'div',
22655             // item is needed for carousel - not sure if it has any effect otherwise
22656             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22657             html: this.html || ''
22658         };
22659         
22660         if(this.active){
22661             cfg.cls += ' active';
22662         }
22663         
22664         if(this.tabId){
22665             cfg.tabId = this.tabId;
22666         }
22667         
22668         
22669         
22670         return cfg;
22671     },
22672     
22673     initEvents:  function()
22674     {
22675         var p = this.parent();
22676         
22677         this.navId = this.navId || p.navId;
22678         
22679         if (typeof(this.navId) != 'undefined') {
22680             // not really needed.. but just in case.. parent should be a NavGroup.
22681             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22682             
22683             tg.register(this);
22684             
22685             var i = tg.tabs.length - 1;
22686             
22687             if(this.active && tg.bullets > 0 && i < tg.bullets){
22688                 tg.setActiveBullet(i);
22689             }
22690         }
22691         
22692         this.el.on('click', this.onClick, this);
22693         
22694         if(Roo.isTouch && this.touchSlide){
22695             this.el.on("touchstart", this.onTouchStart, this);
22696             this.el.on("touchmove", this.onTouchMove, this);
22697             this.el.on("touchend", this.onTouchEnd, this);
22698         }
22699         
22700     },
22701     
22702     onRender : function(ct, position)
22703     {
22704         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22705     },
22706     
22707     setActive : function(state)
22708     {
22709         Roo.log("panel - set active " + this.tabId + "=" + state);
22710         
22711         this.active = state;
22712         if (!state) {
22713             this.el.removeClass('active');
22714             
22715         } else  if (!this.el.hasClass('active')) {
22716             this.el.addClass('active');
22717         }
22718         
22719         this.fireEvent('changed', this, state);
22720     },
22721     
22722     onClick : function(e)
22723     {
22724         e.preventDefault();
22725         
22726         if(!this.href.length){
22727             return;
22728         }
22729         
22730         window.location.href = this.href;
22731     },
22732     
22733     startX : 0,
22734     startY : 0,
22735     endX : 0,
22736     endY : 0,
22737     swiping : false,
22738     
22739     onTouchStart : function(e)
22740     {
22741         this.swiping = false;
22742         
22743         this.startX = e.browserEvent.touches[0].clientX;
22744         this.startY = e.browserEvent.touches[0].clientY;
22745     },
22746     
22747     onTouchMove : function(e)
22748     {
22749         this.swiping = true;
22750         
22751         this.endX = e.browserEvent.touches[0].clientX;
22752         this.endY = e.browserEvent.touches[0].clientY;
22753     },
22754     
22755     onTouchEnd : function(e)
22756     {
22757         if(!this.swiping){
22758             this.onClick(e);
22759             return;
22760         }
22761         
22762         var tabGroup = this.parent();
22763         
22764         if(this.endX > this.startX){ // swiping right
22765             tabGroup.showPanelPrev();
22766             return;
22767         }
22768         
22769         if(this.startX > this.endX){ // swiping left
22770             tabGroup.showPanelNext();
22771             return;
22772         }
22773     }
22774     
22775     
22776 });
22777  
22778
22779  
22780
22781  /*
22782  * - LGPL
22783  *
22784  * DateField
22785  * 
22786  */
22787
22788 /**
22789  * @class Roo.bootstrap.form.DateField
22790  * @extends Roo.bootstrap.form.Input
22791  * Bootstrap DateField class
22792  * @cfg {Number} weekStart default 0
22793  * @cfg {String} viewMode default empty, (months|years)
22794  * @cfg {String} minViewMode default empty, (months|years)
22795  * @cfg {Number} startDate default -Infinity
22796  * @cfg {Number} endDate default Infinity
22797  * @cfg {Boolean} todayHighlight default false
22798  * @cfg {Boolean} todayBtn default false
22799  * @cfg {Boolean} calendarWeeks default false
22800  * @cfg {Object} daysOfWeekDisabled default empty
22801  * @cfg {Boolean} singleMode default false (true | false)
22802  * 
22803  * @cfg {Boolean} keyboardNavigation default true
22804  * @cfg {String} language default en
22805  * 
22806  * @constructor
22807  * Create a new DateField
22808  * @param {Object} config The config object
22809  */
22810
22811 Roo.bootstrap.form.DateField = function(config){
22812     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22813      this.addEvents({
22814             /**
22815              * @event show
22816              * Fires when this field show.
22817              * @param {Roo.bootstrap.form.DateField} this
22818              * @param {Mixed} date The date value
22819              */
22820             show : true,
22821             /**
22822              * @event show
22823              * Fires when this field hide.
22824              * @param {Roo.bootstrap.form.DateField} this
22825              * @param {Mixed} date The date value
22826              */
22827             hide : true,
22828             /**
22829              * @event select
22830              * Fires when select a date.
22831              * @param {Roo.bootstrap.form.DateField} this
22832              * @param {Mixed} date The date value
22833              */
22834             select : true,
22835             /**
22836              * @event beforeselect
22837              * Fires when before select a date.
22838              * @param {Roo.bootstrap.form.DateField} this
22839              * @param {Mixed} date The date value
22840              */
22841             beforeselect : true
22842         });
22843 };
22844
22845 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22846     
22847     /**
22848      * @cfg {String} format
22849      * The default date format string which can be overriden for localization support.  The format must be
22850      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22851      */
22852     format : "m/d/y",
22853     /**
22854      * @cfg {String} altFormats
22855      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22856      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22857      */
22858     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22859     
22860     weekStart : 0,
22861     
22862     viewMode : '',
22863     
22864     minViewMode : '',
22865     
22866     todayHighlight : false,
22867     
22868     todayBtn: false,
22869     
22870     language: 'en',
22871     
22872     keyboardNavigation: true,
22873     
22874     calendarWeeks: false,
22875     
22876     startDate: -Infinity,
22877     
22878     endDate: Infinity,
22879     
22880     daysOfWeekDisabled: [],
22881     
22882     _events: [],
22883     
22884     singleMode : false,
22885     
22886     UTCDate: function()
22887     {
22888         return new Date(Date.UTC.apply(Date, arguments));
22889     },
22890     
22891     UTCToday: function()
22892     {
22893         var today = new Date();
22894         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22895     },
22896     
22897     getDate: function() {
22898             var d = this.getUTCDate();
22899             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22900     },
22901     
22902     getUTCDate: function() {
22903             return this.date;
22904     },
22905     
22906     setDate: function(d) {
22907             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22908     },
22909     
22910     setUTCDate: function(d) {
22911             this.date = d;
22912             this.setValue(this.formatDate(this.date));
22913     },
22914         
22915     onRender: function(ct, position)
22916     {
22917         
22918         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22919         
22920         this.language = this.language || 'en';
22921         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22922         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22923         
22924         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22925         this.format = this.format || 'm/d/y';
22926         this.isInline = false;
22927         this.isInput = true;
22928         this.component = this.el.select('.add-on', true).first() || false;
22929         this.component = (this.component && this.component.length === 0) ? false : this.component;
22930         this.hasInput = this.component && this.inputEl().length;
22931         
22932         if (typeof(this.minViewMode === 'string')) {
22933             switch (this.minViewMode) {
22934                 case 'months':
22935                     this.minViewMode = 1;
22936                     break;
22937                 case 'years':
22938                     this.minViewMode = 2;
22939                     break;
22940                 default:
22941                     this.minViewMode = 0;
22942                     break;
22943             }
22944         }
22945         
22946         if (typeof(this.viewMode === 'string')) {
22947             switch (this.viewMode) {
22948                 case 'months':
22949                     this.viewMode = 1;
22950                     break;
22951                 case 'years':
22952                     this.viewMode = 2;
22953                     break;
22954                 default:
22955                     this.viewMode = 0;
22956                     break;
22957             }
22958         }
22959                 
22960         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22961         
22962 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22963         
22964         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22965         
22966         this.picker().on('mousedown', this.onMousedown, this);
22967         this.picker().on('click', this.onClick, this);
22968         
22969         this.picker().addClass('datepicker-dropdown');
22970         
22971         this.startViewMode = this.viewMode;
22972         
22973         if(this.singleMode){
22974             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22975                 v.setVisibilityMode(Roo.Element.DISPLAY);
22976                 v.hide();
22977             });
22978             
22979             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22980                 v.setStyle('width', '189px');
22981             });
22982         }
22983         
22984         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22985             if(!this.calendarWeeks){
22986                 v.remove();
22987                 return;
22988             }
22989             
22990             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22991             v.attr('colspan', function(i, val){
22992                 return parseInt(val) + 1;
22993             });
22994         });
22995                         
22996         
22997         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22998         
22999         this.setStartDate(this.startDate);
23000         this.setEndDate(this.endDate);
23001         
23002         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23003         
23004         this.fillDow();
23005         this.fillMonths();
23006         this.update();
23007         this.showMode();
23008         
23009         if(this.isInline) {
23010             this.showPopup();
23011         }
23012     },
23013     
23014     picker : function()
23015     {
23016         return this.pickerEl;
23017 //        return this.el.select('.datepicker', true).first();
23018     },
23019     
23020     fillDow: function()
23021     {
23022         var dowCnt = this.weekStart;
23023         
23024         var dow = {
23025             tag: 'tr',
23026             cn: [
23027                 
23028             ]
23029         };
23030         
23031         if(this.calendarWeeks){
23032             dow.cn.push({
23033                 tag: 'th',
23034                 cls: 'cw',
23035                 html: '&nbsp;'
23036             })
23037         }
23038         
23039         while (dowCnt < this.weekStart + 7) {
23040             dow.cn.push({
23041                 tag: 'th',
23042                 cls: 'dow',
23043                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23044             });
23045         }
23046         
23047         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23048     },
23049     
23050     fillMonths: function()
23051     {    
23052         var i = 0;
23053         var months = this.picker().select('>.datepicker-months td', true).first();
23054         
23055         months.dom.innerHTML = '';
23056         
23057         while (i < 12) {
23058             var month = {
23059                 tag: 'span',
23060                 cls: 'month',
23061                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23062             };
23063             
23064             months.createChild(month);
23065         }
23066         
23067     },
23068     
23069     update: function()
23070     {
23071         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;
23072         
23073         if (this.date < this.startDate) {
23074             this.viewDate = new Date(this.startDate);
23075         } else if (this.date > this.endDate) {
23076             this.viewDate = new Date(this.endDate);
23077         } else {
23078             this.viewDate = new Date(this.date);
23079         }
23080         
23081         this.fill();
23082     },
23083     
23084     fill: function() 
23085     {
23086         var d = new Date(this.viewDate),
23087                 year = d.getUTCFullYear(),
23088                 month = d.getUTCMonth(),
23089                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23090                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23091                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23092                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23093                 currentDate = this.date && this.date.valueOf(),
23094                 today = this.UTCToday();
23095         
23096         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23097         
23098 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23099         
23100 //        this.picker.select('>tfoot th.today').
23101 //                                              .text(dates[this.language].today)
23102 //                                              .toggle(this.todayBtn !== false);
23103     
23104         this.updateNavArrows();
23105         this.fillMonths();
23106                                                 
23107         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23108         
23109         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23110          
23111         prevMonth.setUTCDate(day);
23112         
23113         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23114         
23115         var nextMonth = new Date(prevMonth);
23116         
23117         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23118         
23119         nextMonth = nextMonth.valueOf();
23120         
23121         var fillMonths = false;
23122         
23123         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23124         
23125         while(prevMonth.valueOf() <= nextMonth) {
23126             var clsName = '';
23127             
23128             if (prevMonth.getUTCDay() === this.weekStart) {
23129                 if(fillMonths){
23130                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23131                 }
23132                     
23133                 fillMonths = {
23134                     tag: 'tr',
23135                     cn: []
23136                 };
23137                 
23138                 if(this.calendarWeeks){
23139                     // ISO 8601: First week contains first thursday.
23140                     // ISO also states week starts on Monday, but we can be more abstract here.
23141                     var
23142                     // Start of current week: based on weekstart/current date
23143                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23144                     // Thursday of this week
23145                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23146                     // First Thursday of year, year from thursday
23147                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23148                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23149                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23150                     
23151                     fillMonths.cn.push({
23152                         tag: 'td',
23153                         cls: 'cw',
23154                         html: calWeek
23155                     });
23156                 }
23157             }
23158             
23159             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23160                 clsName += ' old';
23161             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23162                 clsName += ' new';
23163             }
23164             if (this.todayHighlight &&
23165                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23166                 prevMonth.getUTCMonth() == today.getMonth() &&
23167                 prevMonth.getUTCDate() == today.getDate()) {
23168                 clsName += ' today';
23169             }
23170             
23171             if (currentDate && prevMonth.valueOf() === currentDate) {
23172                 clsName += ' active';
23173             }
23174             
23175             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23176                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23177                     clsName += ' disabled';
23178             }
23179             
23180             fillMonths.cn.push({
23181                 tag: 'td',
23182                 cls: 'day ' + clsName,
23183                 html: prevMonth.getDate()
23184             });
23185             
23186             prevMonth.setDate(prevMonth.getDate()+1);
23187         }
23188           
23189         var currentYear = this.date && this.date.getUTCFullYear();
23190         var currentMonth = this.date && this.date.getUTCMonth();
23191         
23192         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23193         
23194         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23195             v.removeClass('active');
23196             
23197             if(currentYear === year && k === currentMonth){
23198                 v.addClass('active');
23199             }
23200             
23201             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23202                 v.addClass('disabled');
23203             }
23204             
23205         });
23206         
23207         
23208         year = parseInt(year/10, 10) * 10;
23209         
23210         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23211         
23212         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23213         
23214         year -= 1;
23215         for (var i = -1; i < 11; i++) {
23216             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23217                 tag: 'span',
23218                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23219                 html: year
23220             });
23221             
23222             year += 1;
23223         }
23224     },
23225     
23226     showMode: function(dir) 
23227     {
23228         if (dir) {
23229             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23230         }
23231         
23232         Roo.each(this.picker().select('>div',true).elements, function(v){
23233             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23234             v.hide();
23235         });
23236         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23237     },
23238     
23239     place: function()
23240     {
23241         if(this.isInline) {
23242             return;
23243         }
23244         
23245         this.picker().removeClass(['bottom', 'top']);
23246         
23247         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23248             /*
23249              * place to the top of element!
23250              *
23251              */
23252             
23253             this.picker().addClass('top');
23254             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23255             
23256             return;
23257         }
23258         
23259         this.picker().addClass('bottom');
23260         
23261         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23262     },
23263     
23264     parseDate : function(value)
23265     {
23266         if(!value || value instanceof Date){
23267             return value;
23268         }
23269         var v = Date.parseDate(value, this.format);
23270         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23271             v = Date.parseDate(value, 'Y-m-d');
23272         }
23273         if(!v && this.altFormats){
23274             if(!this.altFormatsArray){
23275                 this.altFormatsArray = this.altFormats.split("|");
23276             }
23277             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23278                 v = Date.parseDate(value, this.altFormatsArray[i]);
23279             }
23280         }
23281         return v;
23282     },
23283     
23284     formatDate : function(date, fmt)
23285     {   
23286         return (!date || !(date instanceof Date)) ?
23287         date : date.dateFormat(fmt || this.format);
23288     },
23289     
23290     onFocus : function()
23291     {
23292         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23293         this.showPopup();
23294     },
23295     
23296     onBlur : function()
23297     {
23298         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23299         
23300         var d = this.inputEl().getValue();
23301         
23302         this.setValue(d);
23303                 
23304         this.hidePopup();
23305     },
23306     
23307     showPopup : function()
23308     {
23309         this.picker().show();
23310         this.update();
23311         this.place();
23312         
23313         this.fireEvent('showpopup', this, this.date);
23314     },
23315     
23316     hidePopup : function()
23317     {
23318         if(this.isInline) {
23319             return;
23320         }
23321         this.picker().hide();
23322         this.viewMode = this.startViewMode;
23323         this.showMode();
23324         
23325         this.fireEvent('hidepopup', this, this.date);
23326         
23327     },
23328     
23329     onMousedown: function(e)
23330     {
23331         e.stopPropagation();
23332         e.preventDefault();
23333     },
23334     
23335     keyup: function(e)
23336     {
23337         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23338         this.update();
23339     },
23340
23341     setValue: function(v)
23342     {
23343         if(this.fireEvent('beforeselect', this, v) !== false){
23344             var d = new Date(this.parseDate(v) ).clearTime();
23345         
23346             if(isNaN(d.getTime())){
23347                 this.date = this.viewDate = '';
23348                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23349                 return;
23350             }
23351
23352             v = this.formatDate(d);
23353
23354             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23355
23356             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23357
23358             this.update();
23359
23360             this.fireEvent('select', this, this.date);
23361         }
23362     },
23363     
23364     getValue: function()
23365     {
23366         return this.formatDate(this.date);
23367     },
23368     
23369     fireKey: function(e)
23370     {
23371         if (!this.picker().isVisible()){
23372             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23373                 this.showPopup();
23374             }
23375             return;
23376         }
23377         
23378         var dateChanged = false,
23379         dir, day, month,
23380         newDate, newViewDate;
23381         
23382         switch(e.keyCode){
23383             case 27: // escape
23384                 this.hidePopup();
23385                 e.preventDefault();
23386                 break;
23387             case 37: // left
23388             case 39: // right
23389                 if (!this.keyboardNavigation) {
23390                     break;
23391                 }
23392                 dir = e.keyCode == 37 ? -1 : 1;
23393                 
23394                 if (e.ctrlKey){
23395                     newDate = this.moveYear(this.date, dir);
23396                     newViewDate = this.moveYear(this.viewDate, dir);
23397                 } else if (e.shiftKey){
23398                     newDate = this.moveMonth(this.date, dir);
23399                     newViewDate = this.moveMonth(this.viewDate, dir);
23400                 } else {
23401                     newDate = new Date(this.date);
23402                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23403                     newViewDate = new Date(this.viewDate);
23404                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23405                 }
23406                 if (this.dateWithinRange(newDate)){
23407                     this.date = newDate;
23408                     this.viewDate = newViewDate;
23409                     this.setValue(this.formatDate(this.date));
23410 //                    this.update();
23411                     e.preventDefault();
23412                     dateChanged = true;
23413                 }
23414                 break;
23415             case 38: // up
23416             case 40: // down
23417                 if (!this.keyboardNavigation) {
23418                     break;
23419                 }
23420                 dir = e.keyCode == 38 ? -1 : 1;
23421                 if (e.ctrlKey){
23422                     newDate = this.moveYear(this.date, dir);
23423                     newViewDate = this.moveYear(this.viewDate, dir);
23424                 } else if (e.shiftKey){
23425                     newDate = this.moveMonth(this.date, dir);
23426                     newViewDate = this.moveMonth(this.viewDate, dir);
23427                 } else {
23428                     newDate = new Date(this.date);
23429                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23430                     newViewDate = new Date(this.viewDate);
23431                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23432                 }
23433                 if (this.dateWithinRange(newDate)){
23434                     this.date = newDate;
23435                     this.viewDate = newViewDate;
23436                     this.setValue(this.formatDate(this.date));
23437 //                    this.update();
23438                     e.preventDefault();
23439                     dateChanged = true;
23440                 }
23441                 break;
23442             case 13: // enter
23443                 this.setValue(this.formatDate(this.date));
23444                 this.hidePopup();
23445                 e.preventDefault();
23446                 break;
23447             case 9: // tab
23448                 this.setValue(this.formatDate(this.date));
23449                 this.hidePopup();
23450                 break;
23451             case 16: // shift
23452             case 17: // ctrl
23453             case 18: // alt
23454                 break;
23455             default :
23456                 this.hidePopup();
23457                 
23458         }
23459     },
23460     
23461     
23462     onClick: function(e) 
23463     {
23464         e.stopPropagation();
23465         e.preventDefault();
23466         
23467         var target = e.getTarget();
23468         
23469         if(target.nodeName.toLowerCase() === 'i'){
23470             target = Roo.get(target).dom.parentNode;
23471         }
23472         
23473         var nodeName = target.nodeName;
23474         var className = target.className;
23475         var html = target.innerHTML;
23476         //Roo.log(nodeName);
23477         
23478         switch(nodeName.toLowerCase()) {
23479             case 'th':
23480                 switch(className) {
23481                     case 'switch':
23482                         this.showMode(1);
23483                         break;
23484                     case 'prev':
23485                     case 'next':
23486                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23487                         switch(this.viewMode){
23488                                 case 0:
23489                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23490                                         break;
23491                                 case 1:
23492                                 case 2:
23493                                         this.viewDate = this.moveYear(this.viewDate, dir);
23494                                         break;
23495                         }
23496                         this.fill();
23497                         break;
23498                     case 'today':
23499                         var date = new Date();
23500                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23501 //                        this.fill()
23502                         this.setValue(this.formatDate(this.date));
23503                         
23504                         this.hidePopup();
23505                         break;
23506                 }
23507                 break;
23508             case 'span':
23509                 if (className.indexOf('disabled') < 0) {
23510                 if (!this.viewDate) {
23511                     this.viewDate = new Date();
23512                 }
23513                 this.viewDate.setUTCDate(1);
23514                     if (className.indexOf('month') > -1) {
23515                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23516                     } else {
23517                         var year = parseInt(html, 10) || 0;
23518                         this.viewDate.setUTCFullYear(year);
23519                         
23520                     }
23521                     
23522                     if(this.singleMode){
23523                         this.setValue(this.formatDate(this.viewDate));
23524                         this.hidePopup();
23525                         return;
23526                     }
23527                     
23528                     this.showMode(-1);
23529                     this.fill();
23530                 }
23531                 break;
23532                 
23533             case 'td':
23534                 //Roo.log(className);
23535                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23536                     var day = parseInt(html, 10) || 1;
23537                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23538                         month = (this.viewDate || new Date()).getUTCMonth();
23539
23540                     if (className.indexOf('old') > -1) {
23541                         if(month === 0 ){
23542                             month = 11;
23543                             year -= 1;
23544                         }else{
23545                             month -= 1;
23546                         }
23547                     } else if (className.indexOf('new') > -1) {
23548                         if (month == 11) {
23549                             month = 0;
23550                             year += 1;
23551                         } else {
23552                             month += 1;
23553                         }
23554                     }
23555                     //Roo.log([year,month,day]);
23556                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23557                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23558 //                    this.fill();
23559                     //Roo.log(this.formatDate(this.date));
23560                     this.setValue(this.formatDate(this.date));
23561                     this.hidePopup();
23562                 }
23563                 break;
23564         }
23565     },
23566     
23567     setStartDate: function(startDate)
23568     {
23569         this.startDate = startDate || -Infinity;
23570         if (this.startDate !== -Infinity) {
23571             this.startDate = this.parseDate(this.startDate);
23572         }
23573         this.update();
23574         this.updateNavArrows();
23575     },
23576
23577     setEndDate: function(endDate)
23578     {
23579         this.endDate = endDate || Infinity;
23580         if (this.endDate !== Infinity) {
23581             this.endDate = this.parseDate(this.endDate);
23582         }
23583         this.update();
23584         this.updateNavArrows();
23585     },
23586     
23587     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23588     {
23589         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23590         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23591             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23592         }
23593         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23594             return parseInt(d, 10);
23595         });
23596         this.update();
23597         this.updateNavArrows();
23598     },
23599     
23600     updateNavArrows: function() 
23601     {
23602         if(this.singleMode){
23603             return;
23604         }
23605         
23606         var d = new Date(this.viewDate),
23607         year = d.getUTCFullYear(),
23608         month = d.getUTCMonth();
23609         
23610         Roo.each(this.picker().select('.prev', true).elements, function(v){
23611             v.show();
23612             switch (this.viewMode) {
23613                 case 0:
23614
23615                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23616                         v.hide();
23617                     }
23618                     break;
23619                 case 1:
23620                 case 2:
23621                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23622                         v.hide();
23623                     }
23624                     break;
23625             }
23626         });
23627         
23628         Roo.each(this.picker().select('.next', true).elements, function(v){
23629             v.show();
23630             switch (this.viewMode) {
23631                 case 0:
23632
23633                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23634                         v.hide();
23635                     }
23636                     break;
23637                 case 1:
23638                 case 2:
23639                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23640                         v.hide();
23641                     }
23642                     break;
23643             }
23644         })
23645     },
23646     
23647     moveMonth: function(date, dir)
23648     {
23649         if (!dir) {
23650             return date;
23651         }
23652         var new_date = new Date(date.valueOf()),
23653         day = new_date.getUTCDate(),
23654         month = new_date.getUTCMonth(),
23655         mag = Math.abs(dir),
23656         new_month, test;
23657         dir = dir > 0 ? 1 : -1;
23658         if (mag == 1){
23659             test = dir == -1
23660             // If going back one month, make sure month is not current month
23661             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23662             ? function(){
23663                 return new_date.getUTCMonth() == month;
23664             }
23665             // If going forward one month, make sure month is as expected
23666             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23667             : function(){
23668                 return new_date.getUTCMonth() != new_month;
23669             };
23670             new_month = month + dir;
23671             new_date.setUTCMonth(new_month);
23672             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23673             if (new_month < 0 || new_month > 11) {
23674                 new_month = (new_month + 12) % 12;
23675             }
23676         } else {
23677             // For magnitudes >1, move one month at a time...
23678             for (var i=0; i<mag; i++) {
23679                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23680                 new_date = this.moveMonth(new_date, dir);
23681             }
23682             // ...then reset the day, keeping it in the new month
23683             new_month = new_date.getUTCMonth();
23684             new_date.setUTCDate(day);
23685             test = function(){
23686                 return new_month != new_date.getUTCMonth();
23687             };
23688         }
23689         // Common date-resetting loop -- if date is beyond end of month, make it
23690         // end of month
23691         while (test()){
23692             new_date.setUTCDate(--day);
23693             new_date.setUTCMonth(new_month);
23694         }
23695         return new_date;
23696     },
23697
23698     moveYear: function(date, dir)
23699     {
23700         return this.moveMonth(date, dir*12);
23701     },
23702
23703     dateWithinRange: function(date)
23704     {
23705         return date >= this.startDate && date <= this.endDate;
23706     },
23707
23708     
23709     remove: function() 
23710     {
23711         this.picker().remove();
23712     },
23713     
23714     validateValue : function(value)
23715     {
23716         if(this.getVisibilityEl().hasClass('hidden')){
23717             return true;
23718         }
23719         
23720         if(value.length < 1)  {
23721             if(this.allowBlank){
23722                 return true;
23723             }
23724             return false;
23725         }
23726         
23727         if(value.length < this.minLength){
23728             return false;
23729         }
23730         if(value.length > this.maxLength){
23731             return false;
23732         }
23733         if(this.vtype){
23734             var vt = Roo.form.VTypes;
23735             if(!vt[this.vtype](value, this)){
23736                 return false;
23737             }
23738         }
23739         if(typeof this.validator == "function"){
23740             var msg = this.validator(value);
23741             if(msg !== true){
23742                 return false;
23743             }
23744         }
23745         
23746         if(this.regex && !this.regex.test(value)){
23747             return false;
23748         }
23749         
23750         if(typeof(this.parseDate(value)) == 'undefined'){
23751             return false;
23752         }
23753         
23754         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23755             return false;
23756         }      
23757         
23758         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23759             return false;
23760         } 
23761         
23762         
23763         return true;
23764     },
23765     
23766     reset : function()
23767     {
23768         this.date = this.viewDate = '';
23769         
23770         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23771     }
23772    
23773 });
23774
23775 Roo.apply(Roo.bootstrap.form.DateField,  {
23776     
23777     head : {
23778         tag: 'thead',
23779         cn: [
23780         {
23781             tag: 'tr',
23782             cn: [
23783             {
23784                 tag: 'th',
23785                 cls: 'prev',
23786                 html: '<i class="fa fa-arrow-left"/>'
23787             },
23788             {
23789                 tag: 'th',
23790                 cls: 'switch',
23791                 colspan: '5'
23792             },
23793             {
23794                 tag: 'th',
23795                 cls: 'next',
23796                 html: '<i class="fa fa-arrow-right"/>'
23797             }
23798
23799             ]
23800         }
23801         ]
23802     },
23803     
23804     content : {
23805         tag: 'tbody',
23806         cn: [
23807         {
23808             tag: 'tr',
23809             cn: [
23810             {
23811                 tag: 'td',
23812                 colspan: '7'
23813             }
23814             ]
23815         }
23816         ]
23817     },
23818     
23819     footer : {
23820         tag: 'tfoot',
23821         cn: [
23822         {
23823             tag: 'tr',
23824             cn: [
23825             {
23826                 tag: 'th',
23827                 colspan: '7',
23828                 cls: 'today'
23829             }
23830                     
23831             ]
23832         }
23833         ]
23834     },
23835     
23836     dates:{
23837         en: {
23838             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23839             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23840             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23841             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23842             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23843             today: "Today"
23844         }
23845     },
23846     
23847     modes: [
23848     {
23849         clsName: 'days',
23850         navFnc: 'Month',
23851         navStep: 1
23852     },
23853     {
23854         clsName: 'months',
23855         navFnc: 'FullYear',
23856         navStep: 1
23857     },
23858     {
23859         clsName: 'years',
23860         navFnc: 'FullYear',
23861         navStep: 10
23862     }]
23863 });
23864
23865 Roo.apply(Roo.bootstrap.form.DateField,  {
23866   
23867     template : {
23868         tag: 'div',
23869         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23870         cn: [
23871         {
23872             tag: 'div',
23873             cls: 'datepicker-days',
23874             cn: [
23875             {
23876                 tag: 'table',
23877                 cls: 'table-condensed',
23878                 cn:[
23879                 Roo.bootstrap.form.DateField.head,
23880                 {
23881                     tag: 'tbody'
23882                 },
23883                 Roo.bootstrap.form.DateField.footer
23884                 ]
23885             }
23886             ]
23887         },
23888         {
23889             tag: 'div',
23890             cls: 'datepicker-months',
23891             cn: [
23892             {
23893                 tag: 'table',
23894                 cls: 'table-condensed',
23895                 cn:[
23896                 Roo.bootstrap.form.DateField.head,
23897                 Roo.bootstrap.form.DateField.content,
23898                 Roo.bootstrap.form.DateField.footer
23899                 ]
23900             }
23901             ]
23902         },
23903         {
23904             tag: 'div',
23905             cls: 'datepicker-years',
23906             cn: [
23907             {
23908                 tag: 'table',
23909                 cls: 'table-condensed',
23910                 cn:[
23911                 Roo.bootstrap.form.DateField.head,
23912                 Roo.bootstrap.form.DateField.content,
23913                 Roo.bootstrap.form.DateField.footer
23914                 ]
23915             }
23916             ]
23917         }
23918         ]
23919     }
23920 });
23921
23922  
23923
23924  /*
23925  * - LGPL
23926  *
23927  * TimeField
23928  * 
23929  */
23930
23931 /**
23932  * @class Roo.bootstrap.form.TimeField
23933  * @extends Roo.bootstrap.form.Input
23934  * Bootstrap DateField class
23935  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23936  * 
23937  * 
23938  * @constructor
23939  * Create a new TimeField
23940  * @param {Object} config The config object
23941  */
23942
23943 Roo.bootstrap.form.TimeField = function(config){
23944     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23945     this.addEvents({
23946             /**
23947              * @event show
23948              * Fires when this field show.
23949              * @param {Roo.bootstrap.form.DateField} thisthis
23950              * @param {Mixed} date The date value
23951              */
23952             show : true,
23953             /**
23954              * @event show
23955              * Fires when this field hide.
23956              * @param {Roo.bootstrap.form.DateField} this
23957              * @param {Mixed} date The date value
23958              */
23959             hide : true,
23960             /**
23961              * @event select
23962              * Fires when select a date.
23963              * @param {Roo.bootstrap.form.DateField} this
23964              * @param {Mixed} date The date value
23965              */
23966             select : true
23967         });
23968 };
23969
23970 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23971     
23972     /**
23973      * @cfg {String} format
23974      * The default time format string which can be overriden for localization support.  The format must be
23975      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23976      */
23977     format : "H:i",
23978     minuteStep : 1,
23979
23980     getAutoCreate : function()
23981     {
23982         this.after = '<i class="fa far fa-clock"></i>';
23983         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23984         
23985          
23986     },
23987     onRender: function(ct, position)
23988     {
23989         
23990         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23991                 
23992         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23993         
23994         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23995         
23996         this.pop = this.picker().select('>.datepicker-time',true).first();
23997         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23998         
23999         this.picker().on('mousedown', this.onMousedown, this);
24000         this.picker().on('click', this.onClick, this);
24001         
24002         this.picker().addClass('datepicker-dropdown');
24003     
24004         this.fillTime();
24005         this.update();
24006             
24007         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24008         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24009         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24010         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24011         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24012         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24013
24014     },
24015     
24016     fireKey: function(e){
24017         if (!this.picker().isVisible()){
24018             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24019                 this.show();
24020             }
24021             return;
24022         }
24023
24024         e.preventDefault();
24025         
24026         switch(e.keyCode){
24027             case 27: // escape
24028                 this.hide();
24029                 break;
24030             case 37: // left
24031             case 39: // right
24032                 this.onTogglePeriod();
24033                 break;
24034             case 38: // up
24035                 this.onIncrementMinutes();
24036                 break;
24037             case 40: // down
24038                 this.onDecrementMinutes();
24039                 break;
24040             case 13: // enter
24041             case 9: // tab
24042                 this.setTime();
24043                 break;
24044         }
24045     },
24046     
24047     onClick: function(e) {
24048         e.stopPropagation();
24049         e.preventDefault();
24050     },
24051     
24052     picker : function()
24053     {
24054         return this.pickerEl;
24055     },
24056     
24057     fillTime: function()
24058     {    
24059         var time = this.pop.select('tbody', true).first();
24060         
24061         time.dom.innerHTML = '';
24062         
24063         time.createChild({
24064             tag: 'tr',
24065             cn: [
24066                 {
24067                     tag: 'td',
24068                     cn: [
24069                         {
24070                             tag: 'a',
24071                             href: '#',
24072                             cls: 'btn',
24073                             cn: [
24074                                 {
24075                                     tag: 'i',
24076                                     cls: 'hours-up fa fas fa-chevron-up'
24077                                 }
24078                             ]
24079                         } 
24080                     ]
24081                 },
24082                 {
24083                     tag: 'td',
24084                     cls: 'separator'
24085                 },
24086                 {
24087                     tag: 'td',
24088                     cn: [
24089                         {
24090                             tag: 'a',
24091                             href: '#',
24092                             cls: 'btn',
24093                             cn: [
24094                                 {
24095                                     tag: 'i',
24096                                     cls: 'minutes-up fa fas fa-chevron-up'
24097                                 }
24098                             ]
24099                         }
24100                     ]
24101                 },
24102                 {
24103                     tag: 'td',
24104                     cls: 'separator'
24105                 }
24106             ]
24107         });
24108         
24109         time.createChild({
24110             tag: 'tr',
24111             cn: [
24112                 {
24113                     tag: 'td',
24114                     cn: [
24115                         {
24116                             tag: 'span',
24117                             cls: 'timepicker-hour',
24118                             html: '00'
24119                         }  
24120                     ]
24121                 },
24122                 {
24123                     tag: 'td',
24124                     cls: 'separator',
24125                     html: ':'
24126                 },
24127                 {
24128                     tag: 'td',
24129                     cn: [
24130                         {
24131                             tag: 'span',
24132                             cls: 'timepicker-minute',
24133                             html: '00'
24134                         }  
24135                     ]
24136                 },
24137                 {
24138                     tag: 'td',
24139                     cls: 'separator'
24140                 },
24141                 {
24142                     tag: 'td',
24143                     cn: [
24144                         {
24145                             tag: 'button',
24146                             type: 'button',
24147                             cls: 'btn btn-primary period',
24148                             html: 'AM'
24149                             
24150                         }
24151                     ]
24152                 }
24153             ]
24154         });
24155         
24156         time.createChild({
24157             tag: 'tr',
24158             cn: [
24159                 {
24160                     tag: 'td',
24161                     cn: [
24162                         {
24163                             tag: 'a',
24164                             href: '#',
24165                             cls: 'btn',
24166                             cn: [
24167                                 {
24168                                     tag: 'span',
24169                                     cls: 'hours-down fa fas fa-chevron-down'
24170                                 }
24171                             ]
24172                         }
24173                     ]
24174                 },
24175                 {
24176                     tag: 'td',
24177                     cls: 'separator'
24178                 },
24179                 {
24180                     tag: 'td',
24181                     cn: [
24182                         {
24183                             tag: 'a',
24184                             href: '#',
24185                             cls: 'btn',
24186                             cn: [
24187                                 {
24188                                     tag: 'span',
24189                                     cls: 'minutes-down fa fas fa-chevron-down'
24190                                 }
24191                             ]
24192                         }
24193                     ]
24194                 },
24195                 {
24196                     tag: 'td',
24197                     cls: 'separator'
24198                 }
24199             ]
24200         });
24201         
24202     },
24203     
24204     update: function()
24205     {
24206         
24207         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24208         
24209         this.fill();
24210     },
24211     
24212     fill: function() 
24213     {
24214         var hours = this.time.getHours();
24215         var minutes = this.time.getMinutes();
24216         var period = 'AM';
24217         
24218         if(hours > 11){
24219             period = 'PM';
24220         }
24221         
24222         if(hours == 0){
24223             hours = 12;
24224         }
24225         
24226         
24227         if(hours > 12){
24228             hours = hours - 12;
24229         }
24230         
24231         if(hours < 10){
24232             hours = '0' + hours;
24233         }
24234         
24235         if(minutes < 10){
24236             minutes = '0' + minutes;
24237         }
24238         
24239         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24240         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24241         this.pop.select('button', true).first().dom.innerHTML = period;
24242         
24243     },
24244     
24245     place: function()
24246     {   
24247         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24248         
24249         var cls = ['bottom'];
24250         
24251         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24252             cls.pop();
24253             cls.push('top');
24254         }
24255         
24256         cls.push('right');
24257         
24258         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24259             cls.pop();
24260             cls.push('left');
24261         }
24262         //this.picker().setXY(20000,20000);
24263         this.picker().addClass(cls.join('-'));
24264         
24265         var _this = this;
24266         
24267         Roo.each(cls, function(c){
24268             if(c == 'bottom'){
24269                 (function() {
24270                  //  
24271                 }).defer(200);
24272                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24273                 //_this.picker().setTop(_this.inputEl().getHeight());
24274                 return;
24275             }
24276             if(c == 'top'){
24277                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24278                 
24279                 //_this.picker().setTop(0 - _this.picker().getHeight());
24280                 return;
24281             }
24282             /*
24283             if(c == 'left'){
24284                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24285                 return;
24286             }
24287             if(c == 'right'){
24288                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24289                 return;
24290             }
24291             */
24292         });
24293         
24294     },
24295   
24296     onFocus : function()
24297     {
24298         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24299         this.show();
24300     },
24301     
24302     onBlur : function()
24303     {
24304         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24305         this.hide();
24306     },
24307     
24308     show : function()
24309     {
24310         this.picker().show();
24311         this.pop.show();
24312         this.update();
24313         this.place();
24314         
24315         this.fireEvent('show', this, this.date);
24316     },
24317     
24318     hide : function()
24319     {
24320         this.picker().hide();
24321         this.pop.hide();
24322         
24323         this.fireEvent('hide', this, this.date);
24324     },
24325     
24326     setTime : function()
24327     {
24328         this.hide();
24329         this.setValue(this.time.format(this.format));
24330         
24331         this.fireEvent('select', this, this.date);
24332         
24333         
24334     },
24335     
24336     onMousedown: function(e){
24337         e.stopPropagation();
24338         e.preventDefault();
24339     },
24340     
24341     onIncrementHours: function()
24342     {
24343         Roo.log('onIncrementHours');
24344         this.time = this.time.add(Date.HOUR, 1);
24345         this.update();
24346         
24347     },
24348     
24349     onDecrementHours: function()
24350     {
24351         Roo.log('onDecrementHours');
24352         this.time = this.time.add(Date.HOUR, -1);
24353         this.update();
24354     },
24355     
24356     onIncrementMinutes: function()
24357     {
24358         Roo.log('onIncrementMinutes');
24359         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24360         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24361         this.update();
24362     },
24363     
24364     onDecrementMinutes: function()
24365     {
24366         Roo.log('onDecrementMinutes');
24367         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24368         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24369         this.update();
24370     },
24371     
24372     onTogglePeriod: function()
24373     {
24374         Roo.log('onTogglePeriod');
24375         this.time = this.time.add(Date.HOUR, 12);
24376         this.update();
24377     }
24378     
24379    
24380 });
24381  
24382
24383 Roo.apply(Roo.bootstrap.form.TimeField,  {
24384   
24385     template : {
24386         tag: 'div',
24387         cls: 'datepicker dropdown-menu',
24388         cn: [
24389             {
24390                 tag: 'div',
24391                 cls: 'datepicker-time',
24392                 cn: [
24393                 {
24394                     tag: 'table',
24395                     cls: 'table-condensed',
24396                     cn:[
24397                         {
24398                             tag: 'tbody',
24399                             cn: [
24400                                 {
24401                                     tag: 'tr',
24402                                     cn: [
24403                                     {
24404                                         tag: 'td',
24405                                         colspan: '7'
24406                                     }
24407                                     ]
24408                                 }
24409                             ]
24410                         },
24411                         {
24412                             tag: 'tfoot',
24413                             cn: [
24414                                 {
24415                                     tag: 'tr',
24416                                     cn: [
24417                                     {
24418                                         tag: 'th',
24419                                         colspan: '7',
24420                                         cls: '',
24421                                         cn: [
24422                                             {
24423                                                 tag: 'button',
24424                                                 cls: 'btn btn-info ok',
24425                                                 html: 'OK'
24426                                             }
24427                                         ]
24428                                     }
24429                     
24430                                     ]
24431                                 }
24432                             ]
24433                         }
24434                     ]
24435                 }
24436                 ]
24437             }
24438         ]
24439     }
24440 });
24441
24442  
24443
24444  /*
24445  * - LGPL
24446  *
24447  * MonthField
24448  * 
24449  */
24450
24451 /**
24452  * @class Roo.bootstrap.form.MonthField
24453  * @extends Roo.bootstrap.form.Input
24454  * Bootstrap MonthField class
24455  * 
24456  * @cfg {String} language default en
24457  * 
24458  * @constructor
24459  * Create a new MonthField
24460  * @param {Object} config The config object
24461  */
24462
24463 Roo.bootstrap.form.MonthField = function(config){
24464     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24465     
24466     this.addEvents({
24467         /**
24468          * @event show
24469          * Fires when this field show.
24470          * @param {Roo.bootstrap.form.MonthField} this
24471          * @param {Mixed} date The date value
24472          */
24473         show : true,
24474         /**
24475          * @event show
24476          * Fires when this field hide.
24477          * @param {Roo.bootstrap.form.MonthField} this
24478          * @param {Mixed} date The date value
24479          */
24480         hide : true,
24481         /**
24482          * @event select
24483          * Fires when select a date.
24484          * @param {Roo.bootstrap.form.MonthField} this
24485          * @param {String} oldvalue The old value
24486          * @param {String} newvalue The new value
24487          */
24488         select : true
24489     });
24490 };
24491
24492 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24493     
24494     onRender: function(ct, position)
24495     {
24496         
24497         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24498         
24499         this.language = this.language || 'en';
24500         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24501         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24502         
24503         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24504         this.isInline = false;
24505         this.isInput = true;
24506         this.component = this.el.select('.add-on', true).first() || false;
24507         this.component = (this.component && this.component.length === 0) ? false : this.component;
24508         this.hasInput = this.component && this.inputEL().length;
24509         
24510         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24511         
24512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24513         
24514         this.picker().on('mousedown', this.onMousedown, this);
24515         this.picker().on('click', this.onClick, this);
24516         
24517         this.picker().addClass('datepicker-dropdown');
24518         
24519         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24520             v.setStyle('width', '189px');
24521         });
24522         
24523         this.fillMonths();
24524         
24525         this.update();
24526         
24527         if(this.isInline) {
24528             this.show();
24529         }
24530         
24531     },
24532     
24533     setValue: function(v, suppressEvent)
24534     {   
24535         var o = this.getValue();
24536         
24537         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24538         
24539         this.update();
24540
24541         if(suppressEvent !== true){
24542             this.fireEvent('select', this, o, v);
24543         }
24544         
24545     },
24546     
24547     getValue: function()
24548     {
24549         return this.value;
24550     },
24551     
24552     onClick: function(e) 
24553     {
24554         e.stopPropagation();
24555         e.preventDefault();
24556         
24557         var target = e.getTarget();
24558         
24559         if(target.nodeName.toLowerCase() === 'i'){
24560             target = Roo.get(target).dom.parentNode;
24561         }
24562         
24563         var nodeName = target.nodeName;
24564         var className = target.className;
24565         var html = target.innerHTML;
24566         
24567         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24568             return;
24569         }
24570         
24571         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24572         
24573         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24574         
24575         this.hide();
24576                         
24577     },
24578     
24579     picker : function()
24580     {
24581         return this.pickerEl;
24582     },
24583     
24584     fillMonths: function()
24585     {    
24586         var i = 0;
24587         var months = this.picker().select('>.datepicker-months td', true).first();
24588         
24589         months.dom.innerHTML = '';
24590         
24591         while (i < 12) {
24592             var month = {
24593                 tag: 'span',
24594                 cls: 'month',
24595                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24596             };
24597             
24598             months.createChild(month);
24599         }
24600         
24601     },
24602     
24603     update: function()
24604     {
24605         var _this = this;
24606         
24607         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24608             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24609         }
24610         
24611         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24612             e.removeClass('active');
24613             
24614             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24615                 e.addClass('active');
24616             }
24617         })
24618     },
24619     
24620     place: function()
24621     {
24622         if(this.isInline) {
24623             return;
24624         }
24625         
24626         this.picker().removeClass(['bottom', 'top']);
24627         
24628         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24629             /*
24630              * place to the top of element!
24631              *
24632              */
24633             
24634             this.picker().addClass('top');
24635             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24636             
24637             return;
24638         }
24639         
24640         this.picker().addClass('bottom');
24641         
24642         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24643     },
24644     
24645     onFocus : function()
24646     {
24647         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24648         this.show();
24649     },
24650     
24651     onBlur : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24654         
24655         var d = this.inputEl().getValue();
24656         
24657         this.setValue(d);
24658                 
24659         this.hide();
24660     },
24661     
24662     show : function()
24663     {
24664         this.picker().show();
24665         this.picker().select('>.datepicker-months', true).first().show();
24666         this.update();
24667         this.place();
24668         
24669         this.fireEvent('show', this, this.date);
24670     },
24671     
24672     hide : function()
24673     {
24674         if(this.isInline) {
24675             return;
24676         }
24677         this.picker().hide();
24678         this.fireEvent('hide', this, this.date);
24679         
24680     },
24681     
24682     onMousedown: function(e)
24683     {
24684         e.stopPropagation();
24685         e.preventDefault();
24686     },
24687     
24688     keyup: function(e)
24689     {
24690         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24691         this.update();
24692     },
24693
24694     fireKey: function(e)
24695     {
24696         if (!this.picker().isVisible()){
24697             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24698                 this.show();
24699             }
24700             return;
24701         }
24702         
24703         var dir;
24704         
24705         switch(e.keyCode){
24706             case 27: // escape
24707                 this.hide();
24708                 e.preventDefault();
24709                 break;
24710             case 37: // left
24711             case 39: // right
24712                 dir = e.keyCode == 37 ? -1 : 1;
24713                 
24714                 this.vIndex = this.vIndex + dir;
24715                 
24716                 if(this.vIndex < 0){
24717                     this.vIndex = 0;
24718                 }
24719                 
24720                 if(this.vIndex > 11){
24721                     this.vIndex = 11;
24722                 }
24723                 
24724                 if(isNaN(this.vIndex)){
24725                     this.vIndex = 0;
24726                 }
24727                 
24728                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24729                 
24730                 break;
24731             case 38: // up
24732             case 40: // down
24733                 
24734                 dir = e.keyCode == 38 ? -1 : 1;
24735                 
24736                 this.vIndex = this.vIndex + dir * 4;
24737                 
24738                 if(this.vIndex < 0){
24739                     this.vIndex = 0;
24740                 }
24741                 
24742                 if(this.vIndex > 11){
24743                     this.vIndex = 11;
24744                 }
24745                 
24746                 if(isNaN(this.vIndex)){
24747                     this.vIndex = 0;
24748                 }
24749                 
24750                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24751                 break;
24752                 
24753             case 13: // enter
24754                 
24755                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24756                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 }
24758                 
24759                 this.hide();
24760                 e.preventDefault();
24761                 break;
24762             case 9: // tab
24763                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24764                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24765                 }
24766                 this.hide();
24767                 break;
24768             case 16: // shift
24769             case 17: // ctrl
24770             case 18: // alt
24771                 break;
24772             default :
24773                 this.hide();
24774                 
24775         }
24776     },
24777     
24778     remove: function() 
24779     {
24780         this.picker().remove();
24781     }
24782    
24783 });
24784
24785 Roo.apply(Roo.bootstrap.form.MonthField,  {
24786     
24787     content : {
24788         tag: 'tbody',
24789         cn: [
24790         {
24791             tag: 'tr',
24792             cn: [
24793             {
24794                 tag: 'td',
24795                 colspan: '7'
24796             }
24797             ]
24798         }
24799         ]
24800     },
24801     
24802     dates:{
24803         en: {
24804             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24805             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24806         }
24807     }
24808 });
24809
24810 Roo.apply(Roo.bootstrap.form.MonthField,  {
24811   
24812     template : {
24813         tag: 'div',
24814         cls: 'datepicker dropdown-menu roo-dynamic',
24815         cn: [
24816             {
24817                 tag: 'div',
24818                 cls: 'datepicker-months',
24819                 cn: [
24820                 {
24821                     tag: 'table',
24822                     cls: 'table-condensed',
24823                     cn:[
24824                         Roo.bootstrap.form.DateField.content
24825                     ]
24826                 }
24827                 ]
24828             }
24829         ]
24830     }
24831 });
24832
24833  
24834
24835  
24836  /*
24837  * - LGPL
24838  *
24839  * CheckBox
24840  * 
24841  */
24842
24843 /**
24844  * @class Roo.bootstrap.form.CheckBox
24845  * @extends Roo.bootstrap.form.Input
24846  * Bootstrap CheckBox class
24847  * 
24848  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24849  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24850  * @cfg {String} boxLabel The text that appears beside the checkbox
24851  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24852  * @cfg {Boolean} checked initnal the element
24853  * @cfg {Boolean} inline inline the element (default false)
24854  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24855  * @cfg {String} tooltip label tooltip
24856  * 
24857  * @constructor
24858  * Create a new CheckBox
24859  * @param {Object} config The config object
24860  */
24861
24862 Roo.bootstrap.form.CheckBox = function(config){
24863     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24864    
24865     this.addEvents({
24866         /**
24867         * @event check
24868         * Fires when the element is checked or unchecked.
24869         * @param {Roo.bootstrap.form.CheckBox} this This input
24870         * @param {Boolean} checked The new checked value
24871         */
24872        check : true,
24873        /**
24874         * @event click
24875         * Fires when the element is click.
24876         * @param {Roo.bootstrap.form.CheckBox} this This input
24877         */
24878        click : true
24879     });
24880     
24881 };
24882
24883 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24884   
24885     inputType: 'checkbox',
24886     inputValue: 1,
24887     valueOff: 0,
24888     boxLabel: false,
24889     checked: false,
24890     weight : false,
24891     inline: false,
24892     tooltip : '',
24893     
24894     // checkbox success does not make any sense really.. 
24895     invalidClass : "",
24896     validClass : "",
24897     
24898     
24899     getAutoCreate : function()
24900     {
24901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24902         
24903         var id = Roo.id();
24904         
24905         var cfg = {};
24906         
24907         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24908         
24909         if(this.inline){
24910             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24911         }
24912         
24913         var input =  {
24914             tag: 'input',
24915             id : id,
24916             type : this.inputType,
24917             value : this.inputValue,
24918             cls : 'roo-' + this.inputType, //'form-box',
24919             placeholder : this.placeholder || ''
24920             
24921         };
24922         
24923         if(this.inputType != 'radio'){
24924             var hidden =  {
24925                 tag: 'input',
24926                 type : 'hidden',
24927                 cls : 'roo-hidden-value',
24928                 value : this.checked ? this.inputValue : this.valueOff
24929             };
24930         }
24931         
24932             
24933         if (this.weight) { // Validity check?
24934             cfg.cls += " " + this.inputType + "-" + this.weight;
24935         }
24936         
24937         if (this.disabled) {
24938             input.disabled=true;
24939         }
24940         
24941         if(this.checked){
24942             input.checked = this.checked;
24943         }
24944         
24945         if (this.name) {
24946             
24947             input.name = this.name;
24948             
24949             if(this.inputType != 'radio'){
24950                 hidden.name = this.name;
24951                 input.name = '_hidden_' + this.name;
24952             }
24953         }
24954         
24955         if (this.size) {
24956             input.cls += ' input-' + this.size;
24957         }
24958         
24959         var settings=this;
24960         
24961         ['xs','sm','md','lg'].map(function(size){
24962             if (settings[size]) {
24963                 cfg.cls += ' col-' + size + '-' + settings[size];
24964             }
24965         });
24966         
24967         var inputblock = input;
24968          
24969         if (this.before || this.after) {
24970             
24971             inputblock = {
24972                 cls : 'input-group',
24973                 cn :  [] 
24974             };
24975             
24976             if (this.before) {
24977                 inputblock.cn.push({
24978                     tag :'span',
24979                     cls : 'input-group-addon',
24980                     html : this.before
24981                 });
24982             }
24983             
24984             inputblock.cn.push(input);
24985             
24986             if(this.inputType != 'radio'){
24987                 inputblock.cn.push(hidden);
24988             }
24989             
24990             if (this.after) {
24991                 inputblock.cn.push({
24992                     tag :'span',
24993                     cls : 'input-group-addon',
24994                     html : this.after
24995                 });
24996             }
24997             
24998         }
24999         var boxLabelCfg = false;
25000         
25001         if(this.boxLabel){
25002            
25003             boxLabelCfg = {
25004                 tag: 'label',
25005                 //'for': id, // box label is handled by onclick - so no for...
25006                 cls: 'box-label',
25007                 html: this.boxLabel
25008             };
25009             if(this.tooltip){
25010                 boxLabelCfg.tooltip = this.tooltip;
25011             }
25012              
25013         }
25014         
25015         
25016         if (align ==='left' && this.fieldLabel.length) {
25017 //                Roo.log("left and has label");
25018             cfg.cn = [
25019                 {
25020                     tag: 'label',
25021                     'for' :  id,
25022                     cls : 'control-label',
25023                     html : this.fieldLabel
25024                 },
25025                 {
25026                     cls : "", 
25027                     cn: [
25028                         inputblock
25029                     ]
25030                 }
25031             ];
25032             
25033             if (boxLabelCfg) {
25034                 cfg.cn[1].cn.push(boxLabelCfg);
25035             }
25036             
25037             if(this.labelWidth > 12){
25038                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25039             }
25040             
25041             if(this.labelWidth < 13 && this.labelmd == 0){
25042                 this.labelmd = this.labelWidth;
25043             }
25044             
25045             if(this.labellg > 0){
25046                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25047                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25048             }
25049             
25050             if(this.labelmd > 0){
25051                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25052                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25053             }
25054             
25055             if(this.labelsm > 0){
25056                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25057                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25058             }
25059             
25060             if(this.labelxs > 0){
25061                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25062                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25063             }
25064             
25065         } else if ( this.fieldLabel.length) {
25066 //                Roo.log(" label");
25067                 cfg.cn = [
25068                    
25069                     {
25070                         tag: this.boxLabel ? 'span' : 'label',
25071                         'for': id,
25072                         cls: 'control-label box-input-label',
25073                         //cls : 'input-group-addon',
25074                         html : this.fieldLabel
25075                     },
25076                     
25077                     inputblock
25078                     
25079                 ];
25080                 if (boxLabelCfg) {
25081                     cfg.cn.push(boxLabelCfg);
25082                 }
25083
25084         } else {
25085             
25086 //                Roo.log(" no label && no align");
25087                 cfg.cn = [  inputblock ] ;
25088                 if (boxLabelCfg) {
25089                     cfg.cn.push(boxLabelCfg);
25090                 }
25091
25092                 
25093         }
25094         
25095        
25096         
25097         if(this.inputType != 'radio'){
25098             cfg.cn.push(hidden);
25099         }
25100         
25101         return cfg;
25102         
25103     },
25104     
25105     /**
25106      * return the real input element.
25107      */
25108     inputEl: function ()
25109     {
25110         return this.el.select('input.roo-' + this.inputType,true).first();
25111     },
25112     hiddenEl: function ()
25113     {
25114         return this.el.select('input.roo-hidden-value',true).first();
25115     },
25116     
25117     labelEl: function()
25118     {
25119         return this.el.select('label.control-label',true).first();
25120     },
25121     /* depricated... */
25122     
25123     label: function()
25124     {
25125         return this.labelEl();
25126     },
25127     
25128     boxLabelEl: function()
25129     {
25130         return this.el.select('label.box-label',true).first();
25131     },
25132     
25133     initEvents : function()
25134     {
25135 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25136         
25137         this.inputEl().on('click', this.onClick,  this);
25138         
25139         if (this.boxLabel) { 
25140             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25141         }
25142         
25143         this.startValue = this.getValue();
25144         
25145         if(this.groupId){
25146             Roo.bootstrap.form.CheckBox.register(this);
25147         }
25148     },
25149     
25150     onClick : function(e)
25151     {   
25152         if(this.fireEvent('click', this, e) !== false){
25153             this.setChecked(!this.checked);
25154         }
25155         
25156     },
25157     
25158     setChecked : function(state,suppressEvent)
25159     {
25160         this.startValue = this.getValue();
25161
25162         if(this.inputType == 'radio'){
25163             
25164             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25165                 e.dom.checked = false;
25166             });
25167             
25168             this.inputEl().dom.checked = true;
25169             
25170             this.inputEl().dom.value = this.inputValue;
25171             
25172             if(suppressEvent !== true){
25173                 this.fireEvent('check', this, true);
25174             }
25175             
25176             this.validate();
25177             
25178             return;
25179         }
25180         
25181         this.checked = state;
25182         
25183         this.inputEl().dom.checked = state;
25184         
25185         
25186         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25187         
25188         if(suppressEvent !== true){
25189             this.fireEvent('check', this, state);
25190         }
25191         
25192         this.validate();
25193     },
25194     
25195     getValue : function()
25196     {
25197         if(this.inputType == 'radio'){
25198             return this.getGroupValue();
25199         }
25200         
25201         return this.hiddenEl().dom.value;
25202         
25203     },
25204     
25205     getGroupValue : function()
25206     {
25207         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25208             return '';
25209         }
25210         
25211         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25212     },
25213     
25214     setValue : function(v,suppressEvent)
25215     {
25216         if(this.inputType == 'radio'){
25217             this.setGroupValue(v, suppressEvent);
25218             return;
25219         }
25220         
25221         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25222         
25223         this.validate();
25224     },
25225     
25226     setGroupValue : function(v, suppressEvent)
25227     {
25228         this.startValue = this.getValue();
25229         
25230         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25231             e.dom.checked = false;
25232             
25233             if(e.dom.value == v){
25234                 e.dom.checked = true;
25235             }
25236         });
25237         
25238         if(suppressEvent !== true){
25239             this.fireEvent('check', this, true);
25240         }
25241
25242         this.validate();
25243         
25244         return;
25245     },
25246     
25247     validate : function()
25248     {
25249         if(this.getVisibilityEl().hasClass('hidden')){
25250             return true;
25251         }
25252         
25253         if(
25254                 this.disabled || 
25255                 (this.inputType == 'radio' && this.validateRadio()) ||
25256                 (this.inputType == 'checkbox' && this.validateCheckbox())
25257         ){
25258             this.markValid();
25259             return true;
25260         }
25261         
25262         this.markInvalid();
25263         return false;
25264     },
25265     
25266     validateRadio : function()
25267     {
25268         if(this.getVisibilityEl().hasClass('hidden')){
25269             return true;
25270         }
25271         
25272         if(this.allowBlank){
25273             return true;
25274         }
25275         
25276         var valid = false;
25277         
25278         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25279             if(!e.dom.checked){
25280                 return;
25281             }
25282             
25283             valid = true;
25284             
25285             return false;
25286         });
25287         
25288         return valid;
25289     },
25290     
25291     validateCheckbox : function()
25292     {
25293         if(!this.groupId){
25294             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25295             //return (this.getValue() == this.inputValue) ? true : false;
25296         }
25297         
25298         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25299         
25300         if(!group){
25301             return false;
25302         }
25303         
25304         var r = false;
25305         
25306         for(var i in group){
25307             if(group[i].el.isVisible(true)){
25308                 r = false;
25309                 break;
25310             }
25311             
25312             r = true;
25313         }
25314         
25315         for(var i in group){
25316             if(r){
25317                 break;
25318             }
25319             
25320             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25321         }
25322         
25323         return r;
25324     },
25325     
25326     /**
25327      * Mark this field as valid
25328      */
25329     markValid : function()
25330     {
25331         var _this = this;
25332         
25333         this.fireEvent('valid', this);
25334         
25335         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25336         
25337         if(this.groupId){
25338             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25339         }
25340         
25341         if(label){
25342             label.markValid();
25343         }
25344
25345         if(this.inputType == 'radio'){
25346             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25347                 var fg = e.findParent('.form-group', false, true);
25348                 if (Roo.bootstrap.version == 3) {
25349                     fg.removeClass([_this.invalidClass, _this.validClass]);
25350                     fg.addClass(_this.validClass);
25351                 } else {
25352                     fg.removeClass(['is-valid', 'is-invalid']);
25353                     fg.addClass('is-valid');
25354                 }
25355             });
25356             
25357             return;
25358         }
25359
25360         if(!this.groupId){
25361             var fg = this.el.findParent('.form-group', false, true);
25362             if (Roo.bootstrap.version == 3) {
25363                 fg.removeClass([this.invalidClass, this.validClass]);
25364                 fg.addClass(this.validClass);
25365             } else {
25366                 fg.removeClass(['is-valid', 'is-invalid']);
25367                 fg.addClass('is-valid');
25368             }
25369             return;
25370         }
25371         
25372         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25373         
25374         if(!group){
25375             return;
25376         }
25377         
25378         for(var i in group){
25379             var fg = group[i].el.findParent('.form-group', false, true);
25380             if (Roo.bootstrap.version == 3) {
25381                 fg.removeClass([this.invalidClass, this.validClass]);
25382                 fg.addClass(this.validClass);
25383             } else {
25384                 fg.removeClass(['is-valid', 'is-invalid']);
25385                 fg.addClass('is-valid');
25386             }
25387         }
25388     },
25389     
25390      /**
25391      * Mark this field as invalid
25392      * @param {String} msg The validation message
25393      */
25394     markInvalid : function(msg)
25395     {
25396         if(this.allowBlank){
25397             return;
25398         }
25399         
25400         var _this = this;
25401         
25402         this.fireEvent('invalid', this, msg);
25403         
25404         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25405         
25406         if(this.groupId){
25407             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25408         }
25409         
25410         if(label){
25411             label.markInvalid();
25412         }
25413             
25414         if(this.inputType == 'radio'){
25415             
25416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25417                 var fg = e.findParent('.form-group', false, true);
25418                 if (Roo.bootstrap.version == 3) {
25419                     fg.removeClass([_this.invalidClass, _this.validClass]);
25420                     fg.addClass(_this.invalidClass);
25421                 } else {
25422                     fg.removeClass(['is-invalid', 'is-valid']);
25423                     fg.addClass('is-invalid');
25424                 }
25425             });
25426             
25427             return;
25428         }
25429         
25430         if(!this.groupId){
25431             var fg = this.el.findParent('.form-group', false, true);
25432             if (Roo.bootstrap.version == 3) {
25433                 fg.removeClass([_this.invalidClass, _this.validClass]);
25434                 fg.addClass(_this.invalidClass);
25435             } else {
25436                 fg.removeClass(['is-invalid', 'is-valid']);
25437                 fg.addClass('is-invalid');
25438             }
25439             return;
25440         }
25441         
25442         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25443         
25444         if(!group){
25445             return;
25446         }
25447         
25448         for(var i in group){
25449             var fg = group[i].el.findParent('.form-group', false, true);
25450             if (Roo.bootstrap.version == 3) {
25451                 fg.removeClass([_this.invalidClass, _this.validClass]);
25452                 fg.addClass(_this.invalidClass);
25453             } else {
25454                 fg.removeClass(['is-invalid', 'is-valid']);
25455                 fg.addClass('is-invalid');
25456             }
25457         }
25458         
25459     },
25460     
25461     clearInvalid : function()
25462     {
25463         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25464         
25465         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25466         
25467         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25468         
25469         if (label && label.iconEl) {
25470             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25471             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25472         }
25473     },
25474     
25475     disable : function()
25476     {
25477         if(this.inputType != 'radio'){
25478             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25479             return;
25480         }
25481         
25482         var _this = this;
25483         
25484         if(this.rendered){
25485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25486                 _this.getActionEl().addClass(this.disabledClass);
25487                 e.dom.disabled = true;
25488             });
25489         }
25490         
25491         this.disabled = true;
25492         this.fireEvent("disable", this);
25493         return this;
25494     },
25495
25496     enable : function()
25497     {
25498         if(this.inputType != 'radio'){
25499             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25500             return;
25501         }
25502         
25503         var _this = this;
25504         
25505         if(this.rendered){
25506             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25507                 _this.getActionEl().removeClass(this.disabledClass);
25508                 e.dom.disabled = false;
25509             });
25510         }
25511         
25512         this.disabled = false;
25513         this.fireEvent("enable", this);
25514         return this;
25515     },
25516     
25517     setBoxLabel : function(v)
25518     {
25519         this.boxLabel = v;
25520         
25521         if(this.rendered){
25522             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25523         }
25524     }
25525
25526 });
25527
25528 Roo.apply(Roo.bootstrap.form.CheckBox, {
25529     
25530     groups: {},
25531     
25532      /**
25533     * register a CheckBox Group
25534     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25535     */
25536     register : function(checkbox)
25537     {
25538         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25539             this.groups[checkbox.groupId] = {};
25540         }
25541         
25542         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25543             return;
25544         }
25545         
25546         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25547         
25548     },
25549     /**
25550     * fetch a CheckBox Group based on the group ID
25551     * @param {string} the group ID
25552     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25553     */
25554     get: function(groupId) {
25555         if (typeof(this.groups[groupId]) == 'undefined') {
25556             return false;
25557         }
25558         
25559         return this.groups[groupId] ;
25560     }
25561     
25562     
25563 });
25564 /*
25565  * - LGPL
25566  *
25567  * RadioItem
25568  * 
25569  */
25570
25571 /**
25572  * @class Roo.bootstrap.form.Radio
25573  * @extends Roo.bootstrap.Component
25574  * Bootstrap Radio class
25575  * @cfg {String} boxLabel - the label associated
25576  * @cfg {String} value - the value of radio
25577  * 
25578  * @constructor
25579  * Create a new Radio
25580  * @param {Object} config The config object
25581  */
25582 Roo.bootstrap.form.Radio = function(config){
25583     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25584     
25585 };
25586
25587 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25588     
25589     boxLabel : '',
25590     
25591     value : '',
25592     
25593     getAutoCreate : function()
25594     {
25595         var cfg = {
25596             tag : 'div',
25597             cls : 'form-group radio',
25598             cn : [
25599                 {
25600                     tag : 'label',
25601                     cls : 'box-label',
25602                     html : this.boxLabel
25603                 }
25604             ]
25605         };
25606         
25607         return cfg;
25608     },
25609     
25610     initEvents : function() 
25611     {
25612         this.parent().register(this);
25613         
25614         this.el.on('click', this.onClick, this);
25615         
25616     },
25617     
25618     onClick : function(e)
25619     {
25620         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25621             this.setChecked(true);
25622         }
25623     },
25624     
25625     setChecked : function(state, suppressEvent)
25626     {
25627         this.parent().setValue(this.value, suppressEvent);
25628         
25629     },
25630     
25631     setBoxLabel : function(v)
25632     {
25633         this.boxLabel = v;
25634         
25635         if(this.rendered){
25636             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25637         }
25638     }
25639     
25640 });
25641  
25642
25643  /*
25644  * - LGPL
25645  *
25646  * Input
25647  * 
25648  */
25649
25650 /**
25651  * @class Roo.bootstrap.form.SecurePass
25652  * @extends Roo.bootstrap.form.Input
25653  * Bootstrap SecurePass class
25654  *
25655  * 
25656  * @constructor
25657  * Create a new SecurePass
25658  * @param {Object} config The config object
25659  */
25660  
25661 Roo.bootstrap.form.SecurePass = function (config) {
25662     // these go here, so the translation tool can replace them..
25663     this.errors = {
25664         PwdEmpty: "Please type a password, and then retype it to confirm.",
25665         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25666         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25667         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25668         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25669         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25670         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25671         TooWeak: "Your password is Too Weak."
25672     },
25673     this.meterLabel = "Password strength:";
25674     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25675     this.meterClass = [
25676         "roo-password-meter-tooweak", 
25677         "roo-password-meter-weak", 
25678         "roo-password-meter-medium", 
25679         "roo-password-meter-strong", 
25680         "roo-password-meter-grey"
25681     ];
25682     
25683     this.errors = {};
25684     
25685     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25686 }
25687
25688 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25689     /**
25690      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25691      * {
25692      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25693      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25694      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25695      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25696      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25697      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25698      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25699      * })
25700      */
25701     // private
25702     
25703     meterWidth: 300,
25704     errorMsg :'',    
25705     errors: false,
25706     imageRoot: '/',
25707     /**
25708      * @cfg {String/Object} Label for the strength meter (defaults to
25709      * 'Password strength:')
25710      */
25711     // private
25712     meterLabel: '',
25713     /**
25714      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25715      * ['Weak', 'Medium', 'Strong'])
25716      */
25717     // private    
25718     pwdStrengths: false,    
25719     // private
25720     strength: 0,
25721     // private
25722     _lastPwd: null,
25723     // private
25724     kCapitalLetter: 0,
25725     kSmallLetter: 1,
25726     kDigit: 2,
25727     kPunctuation: 3,
25728     
25729     insecure: false,
25730     // private
25731     initEvents: function ()
25732     {
25733         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25734
25735         if (this.el.is('input[type=password]') && Roo.isSafari) {
25736             this.el.on('keydown', this.SafariOnKeyDown, this);
25737         }
25738
25739         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25740     },
25741     // private
25742     onRender: function (ct, position)
25743     {
25744         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25745         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25746         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25747
25748         this.trigger.createChild({
25749                    cn: [
25750                     {
25751                     //id: 'PwdMeter',
25752                     tag: 'div',
25753                     cls: 'roo-password-meter-grey col-xs-12',
25754                     style: {
25755                         //width: 0,
25756                         //width: this.meterWidth + 'px'                                                
25757                         }
25758                     },
25759                     {                            
25760                          cls: 'roo-password-meter-text'                          
25761                     }
25762                 ]            
25763         });
25764
25765          
25766         if (this.hideTrigger) {
25767             this.trigger.setDisplayed(false);
25768         }
25769         this.setSize(this.width || '', this.height || '');
25770     },
25771     // private
25772     onDestroy: function ()
25773     {
25774         if (this.trigger) {
25775             this.trigger.removeAllListeners();
25776             this.trigger.remove();
25777         }
25778         if (this.wrap) {
25779             this.wrap.remove();
25780         }
25781         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25782     },
25783     // private
25784     checkStrength: function ()
25785     {
25786         var pwd = this.inputEl().getValue();
25787         if (pwd == this._lastPwd) {
25788             return;
25789         }
25790
25791         var strength;
25792         if (this.ClientSideStrongPassword(pwd)) {
25793             strength = 3;
25794         } else if (this.ClientSideMediumPassword(pwd)) {
25795             strength = 2;
25796         } else if (this.ClientSideWeakPassword(pwd)) {
25797             strength = 1;
25798         } else {
25799             strength = 0;
25800         }
25801         
25802         Roo.log('strength1: ' + strength);
25803         
25804         //var pm = this.trigger.child('div/div/div').dom;
25805         var pm = this.trigger.child('div/div');
25806         pm.removeClass(this.meterClass);
25807         pm.addClass(this.meterClass[strength]);
25808                 
25809         
25810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25811                 
25812         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25813         
25814         this._lastPwd = pwd;
25815     },
25816     reset: function ()
25817     {
25818         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25819         
25820         this._lastPwd = '';
25821         
25822         var pm = this.trigger.child('div/div');
25823         pm.removeClass(this.meterClass);
25824         pm.addClass('roo-password-meter-grey');        
25825         
25826         
25827         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25828         
25829         pt.innerHTML = '';
25830         this.inputEl().dom.type='password';
25831     },
25832     // private
25833     validateValue: function (value)
25834     {
25835         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25836             return false;
25837         }
25838         if (value.length == 0) {
25839             if (this.allowBlank) {
25840                 this.clearInvalid();
25841                 return true;
25842             }
25843
25844             this.markInvalid(this.errors.PwdEmpty);
25845             this.errorMsg = this.errors.PwdEmpty;
25846             return false;
25847         }
25848         
25849         if(this.insecure){
25850             return true;
25851         }
25852         
25853         if (!value.match(/[\x21-\x7e]+/)) {
25854             this.markInvalid(this.errors.PwdBadChar);
25855             this.errorMsg = this.errors.PwdBadChar;
25856             return false;
25857         }
25858         if (value.length < 6) {
25859             this.markInvalid(this.errors.PwdShort);
25860             this.errorMsg = this.errors.PwdShort;
25861             return false;
25862         }
25863         if (value.length > 16) {
25864             this.markInvalid(this.errors.PwdLong);
25865             this.errorMsg = this.errors.PwdLong;
25866             return false;
25867         }
25868         var strength;
25869         if (this.ClientSideStrongPassword(value)) {
25870             strength = 3;
25871         } else if (this.ClientSideMediumPassword(value)) {
25872             strength = 2;
25873         } else if (this.ClientSideWeakPassword(value)) {
25874             strength = 1;
25875         } else {
25876             strength = 0;
25877         }
25878
25879         
25880         if (strength < 2) {
25881             //this.markInvalid(this.errors.TooWeak);
25882             this.errorMsg = this.errors.TooWeak;
25883             //return false;
25884         }
25885         
25886         
25887         console.log('strength2: ' + strength);
25888         
25889         //var pm = this.trigger.child('div/div/div').dom;
25890         
25891         var pm = this.trigger.child('div/div');
25892         pm.removeClass(this.meterClass);
25893         pm.addClass(this.meterClass[strength]);
25894                 
25895         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25896                 
25897         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25898         
25899         this.errorMsg = ''; 
25900         return true;
25901     },
25902     // private
25903     CharacterSetChecks: function (type)
25904     {
25905         this.type = type;
25906         this.fResult = false;
25907     },
25908     // private
25909     isctype: function (character, type)
25910     {
25911         switch (type) {  
25912             case this.kCapitalLetter:
25913                 if (character >= 'A' && character <= 'Z') {
25914                     return true;
25915                 }
25916                 break;
25917             
25918             case this.kSmallLetter:
25919                 if (character >= 'a' && character <= 'z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kDigit:
25925                 if (character >= '0' && character <= '9') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kPunctuation:
25931                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             default:
25937                 return false;
25938         }
25939
25940     },
25941     // private
25942     IsLongEnough: function (pwd, size)
25943     {
25944         return !(pwd == null || isNaN(size) || pwd.length < size);
25945     },
25946     // private
25947     SpansEnoughCharacterSets: function (word, nb)
25948     {
25949         if (!this.IsLongEnough(word, nb))
25950         {
25951             return false;
25952         }
25953
25954         var characterSetChecks = new Array(
25955             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25956             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25957         );
25958         
25959         for (var index = 0; index < word.length; ++index) {
25960             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25961                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25962                     characterSetChecks[nCharSet].fResult = true;
25963                     break;
25964                 }
25965             }
25966         }
25967
25968         var nCharSets = 0;
25969         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25970             if (characterSetChecks[nCharSet].fResult) {
25971                 ++nCharSets;
25972             }
25973         }
25974
25975         if (nCharSets < nb) {
25976             return false;
25977         }
25978         return true;
25979     },
25980     // private
25981     ClientSideStrongPassword: function (pwd)
25982     {
25983         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25984     },
25985     // private
25986     ClientSideMediumPassword: function (pwd)
25987     {
25988         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25989     },
25990     // private
25991     ClientSideWeakPassword: function (pwd)
25992     {
25993         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25994     }
25995           
25996 });Roo.rtf = {}; // namespace
25997 Roo.rtf.Hex = function(hex)
25998 {
25999     this.hexstr = hex;
26000 };
26001 Roo.rtf.Paragraph = function(opts)
26002 {
26003     this.content = []; ///??? is that used?
26004 };Roo.rtf.Span = function(opts)
26005 {
26006     this.value = opts.value;
26007 };
26008
26009 Roo.rtf.Group = function(parent)
26010 {
26011     // we dont want to acutally store parent - it will make debug a nightmare..
26012     this.content = [];
26013     this.cn  = [];
26014      
26015        
26016     
26017 };
26018
26019 Roo.rtf.Group.prototype = {
26020     ignorable : false,
26021     content: false,
26022     cn: false,
26023     addContent : function(node) {
26024         // could set styles...
26025         this.content.push(node);
26026     },
26027     addChild : function(cn)
26028     {
26029         this.cn.push(cn);
26030     },
26031     // only for images really...
26032     toDataURL : function()
26033     {
26034         var mimetype = false;
26035         switch(true) {
26036             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26037                 mimetype = "image/png";
26038                 break;
26039              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26040                 mimetype = "image/jpeg";
26041                 break;
26042             default :
26043                 return 'about:blank'; // ?? error?
26044         }
26045         
26046         
26047         var hexstring = this.content[this.content.length-1].value;
26048         
26049         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26050             return String.fromCharCode(parseInt(a, 16));
26051         }).join(""));
26052     }
26053     
26054 };
26055 // this looks like it's normally the {rtf{ .... }}
26056 Roo.rtf.Document = function()
26057 {
26058     // we dont want to acutally store parent - it will make debug a nightmare..
26059     this.rtlch  = [];
26060     this.content = [];
26061     this.cn = [];
26062     
26063 };
26064 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26065     addChild : function(cn)
26066     {
26067         this.cn.push(cn);
26068         switch(cn.type) {
26069             case 'rtlch': // most content seems to be inside this??
26070             case 'listtext':
26071             case 'shpinst':
26072                 this.rtlch.push(cn);
26073                 return;
26074             default:
26075                 this[cn.type] = cn;
26076         }
26077         
26078     },
26079     
26080     getElementsByType : function(type)
26081     {
26082         var ret =  [];
26083         this._getElementsByType(type, ret, this.cn, 'rtf');
26084         return ret;
26085     },
26086     _getElementsByType : function (type, ret, search_array, path)
26087     {
26088         search_array.forEach(function(n,i) {
26089             if (n.type == type) {
26090                 n.path = path + '/' + n.type + ':' + i;
26091                 ret.push(n);
26092             }
26093             if (n.cn.length > 0) {
26094                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26095             }
26096         },this);
26097     }
26098     
26099 });
26100  
26101 Roo.rtf.Ctrl = function(opts)
26102 {
26103     this.value = opts.value;
26104     this.param = opts.param;
26105 };
26106 /**
26107  *
26108  *
26109  * based on this https://github.com/iarna/rtf-parser
26110  * it's really only designed to extract pict from pasted RTF 
26111  *
26112  * usage:
26113  *
26114  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26115  *  
26116  *
26117  */
26118
26119  
26120
26121
26122
26123 Roo.rtf.Parser = function(text) {
26124     //super({objectMode: true})
26125     this.text = '';
26126     this.parserState = this.parseText;
26127     
26128     // these are for interpeter...
26129     this.doc = {};
26130     ///this.parserState = this.parseTop
26131     this.groupStack = [];
26132     this.hexStore = [];
26133     this.doc = false;
26134     
26135     this.groups = []; // where we put the return.
26136     
26137     for (var ii = 0; ii < text.length; ++ii) {
26138         ++this.cpos;
26139         
26140         if (text[ii] === '\n') {
26141             ++this.row;
26142             this.col = 1;
26143         } else {
26144             ++this.col;
26145         }
26146         this.parserState(text[ii]);
26147     }
26148     
26149     
26150     
26151 };
26152 Roo.rtf.Parser.prototype = {
26153     text : '', // string being parsed..
26154     controlWord : '',
26155     controlWordParam :  '',
26156     hexChar : '',
26157     doc : false,
26158     group: false,
26159     groupStack : false,
26160     hexStore : false,
26161     
26162     
26163     cpos : 0, 
26164     row : 1, // reportin?
26165     col : 1, //
26166
26167      
26168     push : function (el)
26169     {
26170         var m = 'cmd'+ el.type;
26171         if (typeof(this[m]) == 'undefined') {
26172             Roo.log('invalid cmd:' + el.type);
26173             return;
26174         }
26175         this[m](el);
26176         //Roo.log(el);
26177     },
26178     flushHexStore : function()
26179     {
26180         if (this.hexStore.length < 1) {
26181             return;
26182         }
26183         var hexstr = this.hexStore.map(
26184             function(cmd) {
26185                 return cmd.value;
26186         }).join('');
26187         
26188         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26189               
26190             
26191         this.hexStore.splice(0)
26192         
26193     },
26194     
26195     cmdgroupstart : function()
26196     {
26197         this.flushHexStore();
26198         if (this.group) {
26199             this.groupStack.push(this.group);
26200         }
26201          // parent..
26202         if (this.doc === false) {
26203             this.group = this.doc = new Roo.rtf.Document();
26204             return;
26205             
26206         }
26207         this.group = new Roo.rtf.Group(this.group);
26208     },
26209     cmdignorable : function()
26210     {
26211         this.flushHexStore();
26212         this.group.ignorable = true;
26213     },
26214     cmdendparagraph : function()
26215     {
26216         this.flushHexStore();
26217         this.group.addContent(new Roo.rtf.Paragraph());
26218     },
26219     cmdgroupend : function ()
26220     {
26221         this.flushHexStore();
26222         var endingGroup = this.group;
26223         
26224         
26225         this.group = this.groupStack.pop();
26226         if (this.group) {
26227             this.group.addChild(endingGroup);
26228         }
26229         
26230         
26231         
26232         var doc = this.group || this.doc;
26233         //if (endingGroup instanceof FontTable) {
26234         //  doc.fonts = endingGroup.table
26235         //} else if (endingGroup instanceof ColorTable) {
26236         //  doc.colors = endingGroup.table
26237         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26238         if (endingGroup.ignorable === false) {
26239             //code
26240             this.groups.push(endingGroup);
26241            // Roo.log( endingGroup );
26242         }
26243             //Roo.each(endingGroup.content, function(item)) {
26244             //    doc.addContent(item);
26245             //}
26246             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26247         //}
26248     },
26249     cmdtext : function (cmd)
26250     {
26251         this.flushHexStore();
26252         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26253             //this.group = this.doc
26254             return;  // we really don't care about stray text...
26255         }
26256         this.group.addContent(new Roo.rtf.Span(cmd));
26257     },
26258     cmdcontrolword : function (cmd)
26259     {
26260         this.flushHexStore();
26261         if (!this.group.type) {
26262             this.group.type = cmd.value;
26263             return;
26264         }
26265         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26266         // we actually don't care about ctrl words...
26267         return ;
26268         /*
26269         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26270         if (this[method]) {
26271             this[method](cmd.param)
26272         } else {
26273             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26274         }
26275         */
26276     },
26277     cmdhexchar : function(cmd) {
26278         this.hexStore.push(cmd);
26279     },
26280     cmderror : function(cmd) {
26281         throw cmd.value;
26282     },
26283     
26284     /*
26285       _flush (done) {
26286         if (this.text !== '\u0000') this.emitText()
26287         done()
26288       }
26289       */
26290       
26291       
26292     parseText : function(c)
26293     {
26294         if (c === '\\') {
26295             this.parserState = this.parseEscapes;
26296         } else if (c === '{') {
26297             this.emitStartGroup();
26298         } else if (c === '}') {
26299             this.emitEndGroup();
26300         } else if (c === '\x0A' || c === '\x0D') {
26301             // cr/lf are noise chars
26302         } else {
26303             this.text += c;
26304         }
26305     },
26306     
26307     parseEscapes: function (c)
26308     {
26309         if (c === '\\' || c === '{' || c === '}') {
26310             this.text += c;
26311             this.parserState = this.parseText;
26312         } else {
26313             this.parserState = this.parseControlSymbol;
26314             this.parseControlSymbol(c);
26315         }
26316     },
26317     parseControlSymbol: function(c)
26318     {
26319         if (c === '~') {
26320             this.text += '\u00a0'; // nbsp
26321             this.parserState = this.parseText
26322         } else if (c === '-') {
26323              this.text += '\u00ad'; // soft hyphen
26324         } else if (c === '_') {
26325             this.text += '\u2011'; // non-breaking hyphen
26326         } else if (c === '*') {
26327             this.emitIgnorable();
26328             this.parserState = this.parseText;
26329         } else if (c === "'") {
26330             this.parserState = this.parseHexChar;
26331         } else if (c === '|') { // formula cacter
26332             this.emitFormula();
26333             this.parserState = this.parseText;
26334         } else if (c === ':') { // subentry in an index entry
26335             this.emitIndexSubEntry();
26336             this.parserState = this.parseText;
26337         } else if (c === '\x0a') {
26338             this.emitEndParagraph();
26339             this.parserState = this.parseText;
26340         } else if (c === '\x0d') {
26341             this.emitEndParagraph();
26342             this.parserState = this.parseText;
26343         } else {
26344             this.parserState = this.parseControlWord;
26345             this.parseControlWord(c);
26346         }
26347     },
26348     parseHexChar: function (c)
26349     {
26350         if (/^[A-Fa-f0-9]$/.test(c)) {
26351             this.hexChar += c;
26352             if (this.hexChar.length >= 2) {
26353               this.emitHexChar();
26354               this.parserState = this.parseText;
26355             }
26356             return;
26357         }
26358         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26359         this.parserState = this.parseText;
26360         
26361     },
26362     parseControlWord : function(c)
26363     {
26364         if (c === ' ') {
26365             this.emitControlWord();
26366             this.parserState = this.parseText;
26367         } else if (/^[-\d]$/.test(c)) {
26368             this.parserState = this.parseControlWordParam;
26369             this.controlWordParam += c;
26370         } else if (/^[A-Za-z]$/.test(c)) {
26371           this.controlWord += c;
26372         } else {
26373           this.emitControlWord();
26374           this.parserState = this.parseText;
26375           this.parseText(c);
26376         }
26377     },
26378     parseControlWordParam : function (c) {
26379         if (/^\d$/.test(c)) {
26380           this.controlWordParam += c;
26381         } else if (c === ' ') {
26382           this.emitControlWord();
26383           this.parserState = this.parseText;
26384         } else {
26385           this.emitControlWord();
26386           this.parserState = this.parseText;
26387           this.parseText(c);
26388         }
26389     },
26390     
26391     
26392     
26393     
26394     emitText : function () {
26395         if (this.text === '') {
26396             return;
26397         }
26398         this.push({
26399             type: 'text',
26400             value: this.text,
26401             pos: this.cpos,
26402             row: this.row,
26403             col: this.col
26404         });
26405         this.text = ''
26406     },
26407     emitControlWord : function ()
26408     {
26409         this.emitText();
26410         if (this.controlWord === '') {
26411             // do we want to track this - it seems just to cause problems.
26412             //this.emitError('empty control word');
26413         } else {
26414             this.push({
26415                   type: 'controlword',
26416                   value: this.controlWord,
26417                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26418                   pos: this.cpos,
26419                   row: this.row,
26420                   col: this.col
26421             });
26422         }
26423         this.controlWord = '';
26424         this.controlWordParam = '';
26425     },
26426     emitStartGroup : function ()
26427     {
26428         this.emitText();
26429         this.push({
26430             type: 'groupstart',
26431             pos: this.cpos,
26432             row: this.row,
26433             col: this.col
26434         });
26435     },
26436     emitEndGroup : function ()
26437     {
26438         this.emitText();
26439         this.push({
26440             type: 'groupend',
26441             pos: this.cpos,
26442             row: this.row,
26443             col: this.col
26444         });
26445     },
26446     emitIgnorable : function ()
26447     {
26448         this.emitText();
26449         this.push({
26450             type: 'ignorable',
26451             pos: this.cpos,
26452             row: this.row,
26453             col: this.col
26454         });
26455     },
26456     emitHexChar : function ()
26457     {
26458         this.emitText();
26459         this.push({
26460             type: 'hexchar',
26461             value: this.hexChar,
26462             pos: this.cpos,
26463             row: this.row,
26464             col: this.col
26465         });
26466         this.hexChar = ''
26467     },
26468     emitError : function (message)
26469     {
26470       this.emitText();
26471       this.push({
26472             type: 'error',
26473             value: message,
26474             row: this.row,
26475             col: this.col,
26476             char: this.cpos //,
26477             //stack: new Error().stack
26478         });
26479     },
26480     emitEndParagraph : function () {
26481         this.emitText();
26482         this.push({
26483             type: 'endparagraph',
26484             pos: this.cpos,
26485             row: this.row,
26486             col: this.col
26487         });
26488     }
26489      
26490 } ;
26491 Roo.htmleditor = {};
26492  
26493 /**
26494  * @class Roo.htmleditor.Filter
26495  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26496  * @cfg {DomElement} node The node to iterate and filter
26497  * @cfg {boolean|String|Array} tag Tags to replace 
26498  * @constructor
26499  * Create a new Filter.
26500  * @param {Object} config Configuration options
26501  */
26502
26503
26504
26505 Roo.htmleditor.Filter = function(cfg) {
26506     Roo.apply(this.cfg);
26507     // this does not actually call walk as it's really just a abstract class
26508 }
26509
26510
26511 Roo.htmleditor.Filter.prototype = {
26512     
26513     node: false,
26514     
26515     tag: false,
26516
26517     // overrride to do replace comments.
26518     replaceComment : false,
26519     
26520     // overrride to do replace or do stuff with tags..
26521     replaceTag : false,
26522     
26523     walk : function(dom)
26524     {
26525         Roo.each( Array.from(dom.childNodes), function( e ) {
26526             switch(true) {
26527                 
26528                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26529                     this.replaceComment(e);
26530                     return;
26531                 
26532                 case e.nodeType != 1: //not a node.
26533                     return;
26534                 
26535                 case this.tag === true: // everything
26536                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26537                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26538                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26539                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26540                     if (this.replaceTag && false === this.replaceTag(e)) {
26541                         return;
26542                     }
26543                     if (e.hasChildNodes()) {
26544                         this.walk(e);
26545                     }
26546                     return;
26547                 
26548                 default:    // tags .. that do not match.
26549                     if (e.hasChildNodes()) {
26550                         this.walk(e);
26551                     }
26552             }
26553             
26554         }, this);
26555         
26556     },
26557     
26558     
26559     removeNodeKeepChildren : function( node)
26560     {
26561     
26562         ar = Array.from(node.childNodes);
26563         for (var i = 0; i < ar.length; i++) {
26564          
26565             node.removeChild(ar[i]);
26566             // what if we need to walk these???
26567             node.parentNode.insertBefore(ar[i], node);
26568            
26569         }
26570         node.parentNode.removeChild(node);
26571     }
26572 }; 
26573
26574 /**
26575  * @class Roo.htmleditor.FilterAttributes
26576  * clean attributes and  styles including http:// etc.. in attribute
26577  * @constructor
26578 * Run a new Attribute Filter
26579 * @param {Object} config Configuration options
26580  */
26581 Roo.htmleditor.FilterAttributes = function(cfg)
26582 {
26583     Roo.apply(this, cfg);
26584     this.attrib_black = this.attrib_black || [];
26585     this.attrib_white = this.attrib_white || [];
26586
26587     this.attrib_clean = this.attrib_clean || [];
26588     this.style_white = this.style_white || [];
26589     this.style_black = this.style_black || [];
26590     this.walk(cfg.node);
26591 }
26592
26593 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26594 {
26595     tag: true, // all tags
26596     
26597     attrib_black : false, // array
26598     attrib_clean : false,
26599     attrib_white : false,
26600
26601     style_white : false,
26602     style_black : false,
26603      
26604      
26605     replaceTag : function(node)
26606     {
26607         if (!node.attributes || !node.attributes.length) {
26608             return true;
26609         }
26610         
26611         for (var i = node.attributes.length-1; i > -1 ; i--) {
26612             var a = node.attributes[i];
26613             //console.log(a);
26614             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26615                 node.removeAttribute(a.name);
26616                 continue;
26617             }
26618             
26619             
26620             
26621             if (a.name.toLowerCase().substr(0,2)=='on')  {
26622                 node.removeAttribute(a.name);
26623                 continue;
26624             }
26625             
26626             
26627             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26628                 node.removeAttribute(a.name);
26629                 continue;
26630             }
26631             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26632                 this.cleanAttr(node,a.name,a.value); // fixme..
26633                 continue;
26634             }
26635             if (a.name == 'style') {
26636                 this.cleanStyle(node,a.name,a.value);
26637                 continue;
26638             }
26639             /// clean up MS crap..
26640             // tecnically this should be a list of valid class'es..
26641             
26642             
26643             if (a.name == 'class') {
26644                 if (a.value.match(/^Mso/)) {
26645                     node.removeAttribute('class');
26646                 }
26647                 
26648                 if (a.value.match(/^body$/)) {
26649                     node.removeAttribute('class');
26650                 }
26651                 continue;
26652             }
26653             
26654             
26655             // style cleanup!?
26656             // class cleanup?
26657             
26658         }
26659         return true; // clean children
26660     },
26661         
26662     cleanAttr: function(node, n,v)
26663     {
26664         
26665         if (v.match(/^\./) || v.match(/^\//)) {
26666             return;
26667         }
26668         if (v.match(/^(http|https):\/\//)
26669             || v.match(/^mailto:/) 
26670             || v.match(/^ftp:/)
26671             || v.match(/^data:/)
26672             ) {
26673             return;
26674         }
26675         if (v.match(/^#/)) {
26676             return;
26677         }
26678         if (v.match(/^\{/)) { // allow template editing.
26679             return;
26680         }
26681 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26682         node.removeAttribute(n);
26683         
26684     },
26685     cleanStyle : function(node,  n,v)
26686     {
26687         if (v.match(/expression/)) { //XSS?? should we even bother..
26688             node.removeAttribute(n);
26689             return;
26690         }
26691         
26692         var parts = v.split(/;/);
26693         var clean = [];
26694         
26695         Roo.each(parts, function(p) {
26696             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26697             if (!p.length) {
26698                 return true;
26699             }
26700             var l = p.split(':').shift().replace(/\s+/g,'');
26701             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26702             
26703             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26704                 return true;
26705             }
26706             //Roo.log()
26707             // only allow 'c whitelisted system attributes'
26708             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26709                 return true;
26710             }
26711             
26712             
26713             clean.push(p);
26714             return true;
26715         },this);
26716         if (clean.length) { 
26717             node.setAttribute(n, clean.join(';'));
26718         } else {
26719             node.removeAttribute(n);
26720         }
26721         
26722     }
26723         
26724         
26725         
26726     
26727 });/**
26728  * @class Roo.htmleditor.FilterBlack
26729  * remove blacklisted elements.
26730  * @constructor
26731  * Run a new Blacklisted Filter
26732  * @param {Object} config Configuration options
26733  */
26734
26735 Roo.htmleditor.FilterBlack = function(cfg)
26736 {
26737     Roo.apply(this, cfg);
26738     this.walk(cfg.node);
26739 }
26740
26741 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26742 {
26743     tag : true, // all elements.
26744    
26745     replaceTag : function(n)
26746     {
26747         n.parentNode.removeChild(n);
26748     }
26749 });
26750 /**
26751  * @class Roo.htmleditor.FilterComment
26752  * remove comments.
26753  * @constructor
26754 * Run a new Comments Filter
26755 * @param {Object} config Configuration options
26756  */
26757 Roo.htmleditor.FilterComment = function(cfg)
26758 {
26759     this.walk(cfg.node);
26760 }
26761
26762 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26763 {
26764   
26765     replaceComment : function(n)
26766     {
26767         n.parentNode.removeChild(n);
26768     }
26769 });/**
26770  * @class Roo.htmleditor.FilterKeepChildren
26771  * remove tags but keep children
26772  * @constructor
26773  * Run a new Keep Children Filter
26774  * @param {Object} config Configuration options
26775  */
26776
26777 Roo.htmleditor.FilterKeepChildren = function(cfg)
26778 {
26779     Roo.apply(this, cfg);
26780     if (this.tag === false) {
26781         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26782     }
26783     // hacky?
26784     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26785         this.cleanNamespace = true;
26786     }
26787         
26788     this.walk(cfg.node);
26789 }
26790
26791 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26792 {
26793     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26794   
26795     replaceTag : function(node)
26796     {
26797         // walk children...
26798         //Roo.log(node.tagName);
26799         var ar = Array.from(node.childNodes);
26800         //remove first..
26801         
26802         for (var i = 0; i < ar.length; i++) {
26803             var e = ar[i];
26804             if (e.nodeType == 1) {
26805                 if (
26806                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26807                     || // array and it matches
26808                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26809                     ||
26810                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26811                     ||
26812                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26813                 ) {
26814                     this.replaceTag(ar[i]); // child is blacklisted as well...
26815                     continue;
26816                 }
26817             }
26818         }  
26819         ar = Array.from(node.childNodes);
26820         for (var i = 0; i < ar.length; i++) {
26821          
26822             node.removeChild(ar[i]);
26823             // what if we need to walk these???
26824             node.parentNode.insertBefore(ar[i], node);
26825             if (this.tag !== false) {
26826                 this.walk(ar[i]);
26827                 
26828             }
26829         }
26830         //Roo.log("REMOVE:" + node.tagName);
26831         node.parentNode.removeChild(node);
26832         return false; // don't walk children
26833         
26834         
26835     }
26836 });/**
26837  * @class Roo.htmleditor.FilterParagraph
26838  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26839  * like on 'push' to remove the <p> tags and replace them with line breaks.
26840  * @constructor
26841  * Run a new Paragraph Filter
26842  * @param {Object} config Configuration options
26843  */
26844
26845 Roo.htmleditor.FilterParagraph = function(cfg)
26846 {
26847     // no need to apply config.
26848     this.walk(cfg.node);
26849 }
26850
26851 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26852 {
26853     
26854      
26855     tag : 'P',
26856     
26857      
26858     replaceTag : function(node)
26859     {
26860         
26861         if (node.childNodes.length == 1 &&
26862             node.childNodes[0].nodeType == 3 &&
26863             node.childNodes[0].textContent.trim().length < 1
26864             ) {
26865             // remove and replace with '<BR>';
26866             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26867             return false; // no need to walk..
26868         }
26869         var ar = Array.from(node.childNodes);
26870         for (var i = 0; i < ar.length; i++) {
26871             node.removeChild(ar[i]);
26872             // what if we need to walk these???
26873             node.parentNode.insertBefore(ar[i], node);
26874         }
26875         // now what about this?
26876         // <p> &nbsp; </p>
26877         
26878         // double BR.
26879         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26880         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26881         node.parentNode.removeChild(node);
26882         
26883         return false;
26884
26885     }
26886     
26887 });/**
26888  * @class Roo.htmleditor.FilterSpan
26889  * filter span's with no attributes out..
26890  * @constructor
26891  * Run a new Span Filter
26892  * @param {Object} config Configuration options
26893  */
26894
26895 Roo.htmleditor.FilterSpan = function(cfg)
26896 {
26897     // no need to apply config.
26898     this.walk(cfg.node);
26899 }
26900
26901 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26902 {
26903      
26904     tag : 'SPAN',
26905      
26906  
26907     replaceTag : function(node)
26908     {
26909         if (node.attributes && node.attributes.length > 0) {
26910             return true; // walk if there are any.
26911         }
26912         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26913         return false;
26914      
26915     }
26916     
26917 });/**
26918  * @class Roo.htmleditor.FilterTableWidth
26919   try and remove table width data - as that frequently messes up other stuff.
26920  * 
26921  *      was cleanTableWidths.
26922  *
26923  * Quite often pasting from word etc.. results in tables with column and widths.
26924  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26925  *
26926  * @constructor
26927  * Run a new Table Filter
26928  * @param {Object} config Configuration options
26929  */
26930
26931 Roo.htmleditor.FilterTableWidth = function(cfg)
26932 {
26933     // no need to apply config.
26934     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26935     this.walk(cfg.node);
26936 }
26937
26938 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26939 {
26940      
26941      
26942     
26943     replaceTag: function(node) {
26944         
26945         
26946       
26947         if (node.hasAttribute('width')) {
26948             node.removeAttribute('width');
26949         }
26950         
26951          
26952         if (node.hasAttribute("style")) {
26953             // pretty basic...
26954             
26955             var styles = node.getAttribute("style").split(";");
26956             var nstyle = [];
26957             Roo.each(styles, function(s) {
26958                 if (!s.match(/:/)) {
26959                     return;
26960                 }
26961                 var kv = s.split(":");
26962                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26963                     return;
26964                 }
26965                 // what ever is left... we allow.
26966                 nstyle.push(s);
26967             });
26968             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26969             if (!nstyle.length) {
26970                 node.removeAttribute('style');
26971             }
26972         }
26973         
26974         return true; // continue doing children..
26975     }
26976 });/**
26977  * @class Roo.htmleditor.FilterWord
26978  * try and clean up all the mess that Word generates.
26979  * 
26980  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26981  
26982  * @constructor
26983  * Run a new Span Filter
26984  * @param {Object} config Configuration options
26985  */
26986
26987 Roo.htmleditor.FilterWord = function(cfg)
26988 {
26989     // no need to apply config.
26990     this.replaceDocBullets(cfg.node);
26991     
26992     this.replaceAname(cfg.node);
26993     // this is disabled as the removal is done by other filters;
26994    // this.walk(cfg.node);
26995     
26996     
26997 }
26998
26999 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27000 {
27001     tag: true,
27002      
27003     
27004     /**
27005      * Clean up MS wordisms...
27006      */
27007     replaceTag : function(node)
27008     {
27009          
27010         // no idea what this does - span with text, replaceds with just text.
27011         if(
27012                 node.nodeName == 'SPAN' &&
27013                 !node.hasAttributes() &&
27014                 node.childNodes.length == 1 &&
27015                 node.firstChild.nodeName == "#text"  
27016         ) {
27017             var textNode = node.firstChild;
27018             node.removeChild(textNode);
27019             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27020                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27021             }
27022             node.parentNode.insertBefore(textNode, node);
27023             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27024                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27025             }
27026             
27027             node.parentNode.removeChild(node);
27028             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27029         }
27030         
27031    
27032         
27033         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27034             node.parentNode.removeChild(node);
27035             return false; // dont do chidlren
27036         }
27037         //Roo.log(node.tagName);
27038         // remove - but keep children..
27039         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27040             //Roo.log('-- removed');
27041             while (node.childNodes.length) {
27042                 var cn = node.childNodes[0];
27043                 node.removeChild(cn);
27044                 node.parentNode.insertBefore(cn, node);
27045                 // move node to parent - and clean it..
27046                 if (cn.nodeType == 1) {
27047                     this.replaceTag(cn);
27048                 }
27049                 
27050             }
27051             node.parentNode.removeChild(node);
27052             /// no need to iterate chidlren = it's got none..
27053             //this.iterateChildren(node, this.cleanWord);
27054             return false; // no need to iterate children.
27055         }
27056         // clean styles
27057         if (node.className.length) {
27058             
27059             var cn = node.className.split(/\W+/);
27060             var cna = [];
27061             Roo.each(cn, function(cls) {
27062                 if (cls.match(/Mso[a-zA-Z]+/)) {
27063                     return;
27064                 }
27065                 cna.push(cls);
27066             });
27067             node.className = cna.length ? cna.join(' ') : '';
27068             if (!cna.length) {
27069                 node.removeAttribute("class");
27070             }
27071         }
27072         
27073         if (node.hasAttribute("lang")) {
27074             node.removeAttribute("lang");
27075         }
27076         
27077         if (node.hasAttribute("style")) {
27078             
27079             var styles = node.getAttribute("style").split(";");
27080             var nstyle = [];
27081             Roo.each(styles, function(s) {
27082                 if (!s.match(/:/)) {
27083                     return;
27084                 }
27085                 var kv = s.split(":");
27086                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27087                     return;
27088                 }
27089                 // what ever is left... we allow.
27090                 nstyle.push(s);
27091             });
27092             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27093             if (!nstyle.length) {
27094                 node.removeAttribute('style');
27095             }
27096         }
27097         return true; // do children
27098         
27099         
27100         
27101     },
27102     
27103     styleToObject: function(node)
27104     {
27105         var styles = (node.getAttribute("style") || '').split(";");
27106         var ret = {};
27107         Roo.each(styles, function(s) {
27108             if (!s.match(/:/)) {
27109                 return;
27110             }
27111             var kv = s.split(":");
27112              
27113             // what ever is left... we allow.
27114             ret[kv[0].trim()] = kv[1];
27115         });
27116         return ret;
27117     },
27118     
27119     
27120     replaceAname : function (doc)
27121     {
27122         // replace all the a/name without..
27123         var aa = Array.from(doc.getElementsByTagName('a'));
27124         for (var i = 0; i  < aa.length; i++) {
27125             var a = aa[i];
27126             if (a.hasAttribute("name")) {
27127                 a.removeAttribute("name");
27128             }
27129             if (a.hasAttribute("href")) {
27130                 continue;
27131             }
27132             // reparent children.
27133             this.removeNodeKeepChildren(a);
27134             
27135         }
27136         
27137         
27138         
27139     },
27140
27141     
27142     
27143     replaceDocBullets : function(doc)
27144     {
27145         // this is a bit odd - but it appears some indents use ql-indent-1
27146          //Roo.log(doc.innerHTML);
27147         
27148         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27149         for( var i = 0; i < listpara.length; i ++) {
27150             listpara[i].className = "MsoListParagraph";
27151         }
27152         
27153         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27154         for( var i = 0; i < listpara.length; i ++) {
27155             listpara[i].className = "MsoListParagraph";
27156         }
27157         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27158         for( var i = 0; i < listpara.length; i ++) {
27159             listpara[i].className = "MsoListParagraph";
27160         }
27161         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27162         for( var i = 0; i < listpara.length; i ++) {
27163             listpara[i].className = "MsoListParagraph";
27164         }
27165         
27166         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27167         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27168         for( var i = 0; i < htwo.length; i ++) {
27169             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27170                 htwo[i].className = "MsoListParagraph";
27171             }
27172         }
27173         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27174         for( var i = 0; i < listpara.length; i ++) {
27175             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27176                 listpara[i].className = "MsoListParagraph";
27177             } else {
27178                 listpara[i].className = "MsoNormalx";
27179             }
27180         }
27181        
27182         listpara = doc.getElementsByClassName('MsoListParagraph');
27183         // Roo.log(doc.innerHTML);
27184         
27185         
27186         
27187         while(listpara.length) {
27188             
27189             this.replaceDocBullet(listpara.item(0));
27190         }
27191       
27192     },
27193     
27194      
27195     
27196     replaceDocBullet : function(p)
27197     {
27198         // gather all the siblings.
27199         var ns = p,
27200             parent = p.parentNode,
27201             doc = parent.ownerDocument,
27202             items = [];
27203             
27204         var listtype = 'ul';   
27205         while (ns) {
27206             if (ns.nodeType != 1) {
27207                 ns = ns.nextSibling;
27208                 continue;
27209             }
27210             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27211                 break;
27212             }
27213             var spans = ns.getElementsByTagName('span');
27214             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27215                 items.push(ns);
27216                 ns = ns.nextSibling;
27217                 has_list = true;
27218                 if (spans.length && spans[0].hasAttribute('style')) {
27219                     var  style = this.styleToObject(spans[0]);
27220                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
27221                         listtype = 'ol';
27222                     }
27223                 }
27224                 
27225                 continue;
27226             }
27227             var spans = ns.getElementsByTagName('span');
27228             if (!spans.length) {
27229                 break;
27230             }
27231             var has_list  = false;
27232             for(var i = 0; i < spans.length; i++) {
27233                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27234                     has_list = true;
27235                     break;
27236                 }
27237             }
27238             if (!has_list) {
27239                 break;
27240             }
27241             items.push(ns);
27242             ns = ns.nextSibling;
27243             
27244             
27245         }
27246         if (!items.length) {
27247             ns.className = "";
27248             return;
27249         }
27250         
27251         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27252         parent.insertBefore(ul, p);
27253         var lvl = 0;
27254         var stack = [ ul ];
27255         var last_li = false;
27256         
27257         var margin_to_depth = {};
27258         max_margins = -1;
27259         
27260         items.forEach(function(n, ipos) {
27261             //Roo.log("got innertHMLT=" + n.innerHTML);
27262             
27263             var spans = n.getElementsByTagName('span');
27264             if (!spans.length) {
27265                 //Roo.log("No spans found");
27266                  
27267                 parent.removeChild(n);
27268                 
27269                 
27270                 return; // skip it...
27271             }
27272            
27273                 
27274             var num = 1;
27275             var style = {};
27276             for(var i = 0; i < spans.length; i++) {
27277             
27278                 style = this.styleToObject(spans[i]);
27279                 if (typeof(style['mso-list']) == 'undefined') {
27280                     continue;
27281                 }
27282                 if (listtype == 'ol') {
27283                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27284                 }
27285                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27286                 break;
27287             }
27288             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27289             style = this.styleToObject(n); // mo-list is from the parent node.
27290             if (typeof(style['mso-list']) == 'undefined') {
27291                 //Roo.log("parent is missing level");
27292                   
27293                 parent.removeChild(n);
27294                  
27295                 return;
27296             }
27297             
27298             var margin = style['margin-left'];
27299             if (typeof(margin_to_depth[margin]) == 'undefined') {
27300                 max_margins++;
27301                 margin_to_depth[margin] = max_margins;
27302             }
27303             nlvl = margin_to_depth[margin] ;
27304              
27305             if (nlvl > lvl) {
27306                 //new indent
27307                 var nul = doc.createElement(listtype); // what about number lists...
27308                 if (!last_li) {
27309                     last_li = doc.createElement('li');
27310                     stack[lvl].appendChild(last_li);
27311                 }
27312                 last_li.appendChild(nul);
27313                 stack[nlvl] = nul;
27314                 
27315             }
27316             lvl = nlvl;
27317             
27318             // not starting at 1..
27319             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27320                 stack[nlvl].setAttribute("start", num);
27321             }
27322             
27323             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27324             last_li = nli;
27325             nli.innerHTML = n.innerHTML;
27326             //Roo.log("innerHTML = " + n.innerHTML);
27327             parent.removeChild(n);
27328             
27329              
27330              
27331             
27332         },this);
27333         
27334         
27335         
27336         
27337     }
27338     
27339     
27340     
27341 });
27342 /**
27343  * @class Roo.htmleditor.FilterStyleToTag
27344  * part of the word stuff... - certain 'styles' should be converted to tags.
27345  * eg.
27346  *   font-weight: bold -> bold
27347  *   ?? super / subscrit etc..
27348  * 
27349  * @constructor
27350 * Run a new style to tag filter.
27351 * @param {Object} config Configuration options
27352  */
27353 Roo.htmleditor.FilterStyleToTag = function(cfg)
27354 {
27355     
27356     this.tags = {
27357         B  : [ 'fontWeight' , 'bold'],
27358         I :  [ 'fontStyle' , 'italic'],
27359         //pre :  [ 'font-style' , 'italic'],
27360         // h1.. h6 ?? font-size?
27361         SUP : [ 'verticalAlign' , 'super' ],
27362         SUB : [ 'verticalAlign' , 'sub' ]
27363         
27364         
27365     };
27366     
27367     Roo.apply(this, cfg);
27368      
27369     
27370     this.walk(cfg.node);
27371     
27372     
27373     
27374 }
27375
27376
27377 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27378 {
27379     tag: true, // all tags
27380     
27381     tags : false,
27382     
27383     
27384     replaceTag : function(node)
27385     {
27386         
27387         
27388         if (node.getAttribute("style") === null) {
27389             return true;
27390         }
27391         var inject = [];
27392         for (var k in this.tags) {
27393             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27394                 inject.push(k);
27395                 node.style.removeProperty(this.tags[k][0]);
27396             }
27397         }
27398         if (!inject.length) {
27399             return true; 
27400         }
27401         var cn = Array.from(node.childNodes);
27402         var nn = node;
27403         Roo.each(inject, function(t) {
27404             var nc = node.ownerDocument.createElement(t);
27405             nn.appendChild(nc);
27406             nn = nc;
27407         });
27408         for(var i = 0;i < cn.length;cn++) {
27409             node.removeChild(cn[i]);
27410             nn.appendChild(cn[i]);
27411         }
27412         return true /// iterate thru
27413     }
27414     
27415 })/**
27416  * @class Roo.htmleditor.FilterLongBr
27417  * BR/BR/BR - keep a maximum of 2...
27418  * @constructor
27419  * Run a new Long BR Filter
27420  * @param {Object} config Configuration options
27421  */
27422
27423 Roo.htmleditor.FilterLongBr = function(cfg)
27424 {
27425     // no need to apply config.
27426     this.walk(cfg.node);
27427 }
27428
27429 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27430 {
27431     
27432      
27433     tag : 'BR',
27434     
27435      
27436     replaceTag : function(node)
27437     {
27438         
27439         var ps = node.nextSibling;
27440         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27441             ps = ps.nextSibling;
27442         }
27443         
27444         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27445             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27446             return false;
27447         }
27448         
27449         if (!ps || ps.nodeType != 1) {
27450             return false;
27451         }
27452         
27453         if (!ps || ps.tagName != 'BR') {
27454            
27455             return false;
27456         }
27457         
27458         
27459         
27460         
27461         
27462         if (!node.previousSibling) {
27463             return false;
27464         }
27465         var ps = node.previousSibling;
27466         
27467         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27468             ps = ps.previousSibling;
27469         }
27470         if (!ps || ps.nodeType != 1) {
27471             return false;
27472         }
27473         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27474         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27475             return false;
27476         }
27477         
27478         node.parentNode.removeChild(node); // remove me...
27479         
27480         return false; // no need to do children
27481
27482     }
27483     
27484 }); 
27485
27486 /**
27487  * @class Roo.htmleditor.FilterBlock
27488  * removes id / data-block and contenteditable that are associated with blocks
27489  * usage should be done on a cloned copy of the dom
27490  * @constructor
27491 * Run a new Attribute Filter { node : xxxx }}
27492 * @param {Object} config Configuration options
27493  */
27494 Roo.htmleditor.FilterBlock = function(cfg)
27495 {
27496     Roo.apply(this, cfg);
27497     var qa = cfg.node.querySelectorAll;
27498     this.removeAttributes('data-block');
27499     this.removeAttributes('contenteditable');
27500     this.removeAttributes('id');
27501     
27502 }
27503
27504 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27505 {
27506     node: true, // all tags
27507      
27508      
27509     removeAttributes : function(attr)
27510     {
27511         var ar = this.node.querySelectorAll('*[' + attr + ']');
27512         for (var i =0;i<ar.length;i++) {
27513             ar[i].removeAttribute(attr);
27514         }
27515     }
27516         
27517         
27518         
27519     
27520 });
27521 /***
27522  * This is based loosely on tinymce 
27523  * @class Roo.htmleditor.TidySerializer
27524  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27525  * @constructor
27526  * @method Serializer
27527  * @param {Object} settings Name/value settings object.
27528  */
27529
27530
27531 Roo.htmleditor.TidySerializer = function(settings)
27532 {
27533     Roo.apply(this, settings);
27534     
27535     this.writer = new Roo.htmleditor.TidyWriter(settings);
27536     
27537     
27538
27539 };
27540 Roo.htmleditor.TidySerializer.prototype = {
27541     
27542     /**
27543      * @param {boolean} inner do the inner of the node.
27544      */
27545     inner : false,
27546     
27547     writer : false,
27548     
27549     /**
27550     * Serializes the specified node into a string.
27551     *
27552     * @example
27553     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27554     * @method serialize
27555     * @param {DomElement} node Node instance to serialize.
27556     * @return {String} String with HTML based on DOM tree.
27557     */
27558     serialize : function(node) {
27559         
27560         // = settings.validate;
27561         var writer = this.writer;
27562         var self  = this;
27563         this.handlers = {
27564             // #text
27565             3: function(node) {
27566                 
27567                 writer.text(node.nodeValue, node);
27568             },
27569             // #comment
27570             8: function(node) {
27571                 writer.comment(node.nodeValue);
27572             },
27573             // Processing instruction
27574             7: function(node) {
27575                 writer.pi(node.name, node.nodeValue);
27576             },
27577             // Doctype
27578             10: function(node) {
27579                 writer.doctype(node.nodeValue);
27580             },
27581             // CDATA
27582             4: function(node) {
27583                 writer.cdata(node.nodeValue);
27584             },
27585             // Document fragment
27586             11: function(node) {
27587                 node = node.firstChild;
27588                 if (!node) {
27589                     return;
27590                 }
27591                 while(node) {
27592                     self.walk(node);
27593                     node = node.nextSibling
27594                 }
27595             }
27596         };
27597         writer.reset();
27598         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27599         return writer.getContent();
27600     },
27601
27602     walk: function(node)
27603     {
27604         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27605             handler = this.handlers[node.nodeType];
27606             
27607         if (handler) {
27608             handler(node);
27609             return;
27610         }
27611     
27612         var name = node.nodeName;
27613         var isEmpty = node.childNodes.length < 1;
27614       
27615         var writer = this.writer;
27616         var attrs = node.attributes;
27617         // Sort attributes
27618         
27619         writer.start(node.nodeName, attrs, isEmpty, node);
27620         if (isEmpty) {
27621             return;
27622         }
27623         node = node.firstChild;
27624         if (!node) {
27625             writer.end(name);
27626             return;
27627         }
27628         while (node) {
27629             this.walk(node);
27630             node = node.nextSibling;
27631         }
27632         writer.end(name);
27633         
27634     
27635     }
27636     // Serialize element and treat all non elements as fragments
27637    
27638 }; 
27639
27640 /***
27641  * This is based loosely on tinymce 
27642  * @class Roo.htmleditor.TidyWriter
27643  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27644  *
27645  * Known issues?
27646  * - not tested much with 'PRE' formated elements.
27647  * 
27648  *
27649  *
27650  */
27651
27652 Roo.htmleditor.TidyWriter = function(settings)
27653 {
27654     
27655     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27656     Roo.apply(this, settings);
27657     this.html = [];
27658     this.state = [];
27659      
27660     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27661   
27662 }
27663 Roo.htmleditor.TidyWriter.prototype = {
27664
27665  
27666     state : false,
27667     
27668     indent :  '  ',
27669     
27670     // part of state...
27671     indentstr : '',
27672     in_pre: false,
27673     in_inline : false,
27674     last_inline : false,
27675     encode : false,
27676      
27677     
27678             /**
27679     * Writes the a start element such as <p id="a">.
27680     *
27681     * @method start
27682     * @param {String} name Name of the element.
27683     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27684     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27685     */
27686     start: function(name, attrs, empty, node)
27687     {
27688         var i, l, attr, value;
27689         
27690         // there are some situations where adding line break && indentation will not work. will not work.
27691         // <span / b / i ... formating?
27692         
27693         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27694         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27695         
27696         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27697         
27698         var add_lb = name == 'BR' ? false : in_inline;
27699         
27700         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27701             i_inline = false;
27702         }
27703
27704         var indentstr =  this.indentstr;
27705         
27706         // e_inline = elements that can be inline, but still allow \n before and after?
27707         // only 'BR' ??? any others?
27708         
27709         // ADD LINE BEFORE tage
27710         if (!this.in_pre) {
27711             if (in_inline) {
27712                 //code
27713                 if (name == 'BR') {
27714                     this.addLine();
27715                 } else if (this.lastElementEndsWS()) {
27716                     this.addLine();
27717                 } else{
27718                     // otherwise - no new line. (and dont indent.)
27719                     indentstr = '';
27720                 }
27721                 
27722             } else {
27723                 this.addLine();
27724             }
27725         } else {
27726             indentstr = '';
27727         }
27728         
27729         this.html.push(indentstr + '<', name.toLowerCase());
27730         
27731         if (attrs) {
27732             for (i = 0, l = attrs.length; i < l; i++) {
27733                 attr = attrs[i];
27734                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27735             }
27736         }
27737      
27738         if (empty) {
27739             if (is_short) {
27740                 this.html[this.html.length] = '/>';
27741             } else {
27742                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27743             }
27744             var e_inline = name == 'BR' ? false : this.in_inline;
27745             
27746             if (!e_inline && !this.in_pre) {
27747                 this.addLine();
27748             }
27749             return;
27750         
27751         }
27752         // not empty..
27753         this.html[this.html.length] = '>';
27754         
27755         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27756         /*
27757         if (!in_inline && !in_pre) {
27758             var cn = node.firstChild;
27759             while(cn) {
27760                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27761                     in_inline = true
27762                     break;
27763                 }
27764                 cn = cn.nextSibling;
27765             }
27766              
27767         }
27768         */
27769         
27770         
27771         this.pushState({
27772             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27773             in_pre : in_pre,
27774             in_inline :  in_inline
27775         });
27776         // add a line after if we are not in a
27777         
27778         if (!in_inline && !in_pre) {
27779             this.addLine();
27780         }
27781         
27782             
27783          
27784         
27785     },
27786     
27787     lastElementEndsWS : function()
27788     {
27789         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27790         if (value === false) {
27791             return true;
27792         }
27793         return value.match(/\s+$/);
27794         
27795     },
27796     
27797     /**
27798      * Writes the a end element such as </p>.
27799      *
27800      * @method end
27801      * @param {String} name Name of the element.
27802      */
27803     end: function(name) {
27804         var value;
27805         this.popState();
27806         var indentstr = '';
27807         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27808         
27809         if (!this.in_pre && !in_inline) {
27810             this.addLine();
27811             indentstr  = this.indentstr;
27812         }
27813         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27814         this.last_inline = in_inline;
27815         
27816         // pop the indent state..
27817     },
27818     /**
27819      * Writes a text node.
27820      *
27821      * In pre - we should not mess with the contents.
27822      * 
27823      *
27824      * @method text
27825      * @param {String} text String to write out.
27826      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27827      */
27828     text: function(in_text, node)
27829     {
27830         // if not in whitespace critical
27831         if (in_text.length < 1) {
27832             return;
27833         }
27834         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27835         
27836         if (this.in_pre) {
27837             this.html[this.html.length] =  text;
27838             return;   
27839         }
27840         
27841         if (this.in_inline) {
27842             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27843             if (text != ' ') {
27844                 text = text.replace(/\s+/,' ');  // all white space to single white space
27845                 
27846                     
27847                 // if next tag is '<BR>', then we can trim right..
27848                 if (node.nextSibling &&
27849                     node.nextSibling.nodeType == 1 &&
27850                     node.nextSibling.nodeName == 'BR' )
27851                 {
27852                     text = text.replace(/\s+$/g,'');
27853                 }
27854                 // if previous tag was a BR, we can also trim..
27855                 if (node.previousSibling &&
27856                     node.previousSibling.nodeType == 1 &&
27857                     node.previousSibling.nodeName == 'BR' )
27858                 {
27859                     text = this.indentstr +  text.replace(/^\s+/g,'');
27860                 }
27861                 if (text.match(/\n/)) {
27862                     text = text.replace(
27863                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27864                     );
27865                     // remoeve the last whitespace / line break.
27866                     text = text.replace(/\n\s+$/,'');
27867                 }
27868                 // repace long lines
27869                 
27870             }
27871              
27872             this.html[this.html.length] =  text;
27873             return;   
27874         }
27875         // see if previous element was a inline element.
27876         var indentstr = this.indentstr;
27877    
27878         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27879         
27880         // should trim left?
27881         if (node.previousSibling &&
27882             node.previousSibling.nodeType == 1 &&
27883             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27884         {
27885             indentstr = '';
27886             
27887         } else {
27888             this.addLine();
27889             text = text.replace(/^\s+/,''); // trim left
27890           
27891         }
27892         // should trim right?
27893         if (node.nextSibling &&
27894             node.nextSibling.nodeType == 1 &&
27895             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27896         {
27897           // noop
27898             
27899         }  else {
27900             text = text.replace(/\s+$/,''); // trim right
27901         }
27902          
27903               
27904         
27905         
27906         
27907         if (text.length < 1) {
27908             return;
27909         }
27910         if (!text.match(/\n/)) {
27911             this.html.push(indentstr + text);
27912             return;
27913         }
27914         
27915         text = this.indentstr + text.replace(
27916             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27917         );
27918         // remoeve the last whitespace / line break.
27919         text = text.replace(/\s+$/,''); 
27920         
27921         this.html.push(text);
27922         
27923         // split and indent..
27924         
27925         
27926     },
27927     /**
27928      * Writes a cdata node such as <![CDATA[data]]>.
27929      *
27930      * @method cdata
27931      * @param {String} text String to write out inside the cdata.
27932      */
27933     cdata: function(text) {
27934         this.html.push('<![CDATA[', text, ']]>');
27935     },
27936     /**
27937     * Writes a comment node such as <!-- Comment -->.
27938     *
27939     * @method cdata
27940     * @param {String} text String to write out inside the comment.
27941     */
27942    comment: function(text) {
27943        this.html.push('<!--', text, '-->');
27944    },
27945     /**
27946      * Writes a PI node such as <?xml attr="value" ?>.
27947      *
27948      * @method pi
27949      * @param {String} name Name of the pi.
27950      * @param {String} text String to write out inside the pi.
27951      */
27952     pi: function(name, text) {
27953         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27954         this.indent != '' && this.html.push('\n');
27955     },
27956     /**
27957      * Writes a doctype node such as <!DOCTYPE data>.
27958      *
27959      * @method doctype
27960      * @param {String} text String to write out inside the doctype.
27961      */
27962     doctype: function(text) {
27963         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27964     },
27965     /**
27966      * Resets the internal buffer if one wants to reuse the writer.
27967      *
27968      * @method reset
27969      */
27970     reset: function() {
27971         this.html.length = 0;
27972         this.state = [];
27973         this.pushState({
27974             indentstr : '',
27975             in_pre : false, 
27976             in_inline : false
27977         })
27978     },
27979     /**
27980      * Returns the contents that got serialized.
27981      *
27982      * @method getContent
27983      * @return {String} HTML contents that got written down.
27984      */
27985     getContent: function() {
27986         return this.html.join('').replace(/\n$/, '');
27987     },
27988     
27989     pushState : function(cfg)
27990     {
27991         this.state.push(cfg);
27992         Roo.apply(this, cfg);
27993     },
27994     
27995     popState : function()
27996     {
27997         if (this.state.length < 1) {
27998             return; // nothing to push
27999         }
28000         var cfg = {
28001             in_pre: false,
28002             indentstr : ''
28003         };
28004         this.state.pop();
28005         if (this.state.length > 0) {
28006             cfg = this.state[this.state.length-1]; 
28007         }
28008         Roo.apply(this, cfg);
28009     },
28010     
28011     addLine: function()
28012     {
28013         if (this.html.length < 1) {
28014             return;
28015         }
28016         
28017         
28018         var value = this.html[this.html.length - 1];
28019         if (value.length > 0 && '\n' !== value) {
28020             this.html.push('\n');
28021         }
28022     }
28023     
28024     
28025 //'pre script noscript style textarea video audio iframe object code'
28026 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28027 // inline 
28028 };
28029
28030 Roo.htmleditor.TidyWriter.inline_elements = [
28031         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28032         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28033 ];
28034 Roo.htmleditor.TidyWriter.shortend_elements = [
28035     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28036     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28037 ];
28038
28039 Roo.htmleditor.TidyWriter.whitespace_elements = [
28040     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28041 ];/***
28042  * This is based loosely on tinymce 
28043  * @class Roo.htmleditor.TidyEntities
28044  * @static
28045  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28046  *
28047  * Not 100% sure this is actually used or needed.
28048  */
28049
28050 Roo.htmleditor.TidyEntities = {
28051     
28052     /**
28053      * initialize data..
28054      */
28055     init : function (){
28056      
28057         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28058        
28059     },
28060
28061
28062     buildEntitiesLookup: function(items, radix) {
28063         var i, chr, entity, lookup = {};
28064         if (!items) {
28065             return {};
28066         }
28067         items = typeof(items) == 'string' ? items.split(',') : items;
28068         radix = radix || 10;
28069         // Build entities lookup table
28070         for (i = 0; i < items.length; i += 2) {
28071             chr = String.fromCharCode(parseInt(items[i], radix));
28072             // Only add non base entities
28073             if (!this.baseEntities[chr]) {
28074                 entity = '&' + items[i + 1] + ';';
28075                 lookup[chr] = entity;
28076                 lookup[entity] = chr;
28077             }
28078         }
28079         return lookup;
28080         
28081     },
28082     
28083     asciiMap : {
28084             128: '€',
28085             130: '‚',
28086             131: 'ƒ',
28087             132: '„',
28088             133: '…',
28089             134: '†',
28090             135: '‡',
28091             136: 'ˆ',
28092             137: '‰',
28093             138: 'Š',
28094             139: '‹',
28095             140: 'Œ',
28096             142: 'Ž',
28097             145: '‘',
28098             146: '’',
28099             147: '“',
28100             148: '”',
28101             149: '•',
28102             150: '–',
28103             151: '—',
28104             152: '˜',
28105             153: '™',
28106             154: 'š',
28107             155: '›',
28108             156: 'œ',
28109             158: 'ž',
28110             159: 'Ÿ'
28111     },
28112     // Raw entities
28113     baseEntities : {
28114         '"': '&quot;',
28115         // Needs to be escaped since the YUI compressor would otherwise break the code
28116         '\'': '&#39;',
28117         '<': '&lt;',
28118         '>': '&gt;',
28119         '&': '&amp;',
28120         '`': '&#96;'
28121     },
28122     // Reverse lookup table for raw entities
28123     reverseEntities : {
28124         '&lt;': '<',
28125         '&gt;': '>',
28126         '&amp;': '&',
28127         '&quot;': '"',
28128         '&apos;': '\''
28129     },
28130     
28131     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28132     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28133     rawCharsRegExp : /[<>&\"\']/g,
28134     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28135     namedEntities  : false,
28136     namedEntitiesData : [ 
28137         '50',
28138         'nbsp',
28139         '51',
28140         'iexcl',
28141         '52',
28142         'cent',
28143         '53',
28144         'pound',
28145         '54',
28146         'curren',
28147         '55',
28148         'yen',
28149         '56',
28150         'brvbar',
28151         '57',
28152         'sect',
28153         '58',
28154         'uml',
28155         '59',
28156         'copy',
28157         '5a',
28158         'ordf',
28159         '5b',
28160         'laquo',
28161         '5c',
28162         'not',
28163         '5d',
28164         'shy',
28165         '5e',
28166         'reg',
28167         '5f',
28168         'macr',
28169         '5g',
28170         'deg',
28171         '5h',
28172         'plusmn',
28173         '5i',
28174         'sup2',
28175         '5j',
28176         'sup3',
28177         '5k',
28178         'acute',
28179         '5l',
28180         'micro',
28181         '5m',
28182         'para',
28183         '5n',
28184         'middot',
28185         '5o',
28186         'cedil',
28187         '5p',
28188         'sup1',
28189         '5q',
28190         'ordm',
28191         '5r',
28192         'raquo',
28193         '5s',
28194         'frac14',
28195         '5t',
28196         'frac12',
28197         '5u',
28198         'frac34',
28199         '5v',
28200         'iquest',
28201         '60',
28202         'Agrave',
28203         '61',
28204         'Aacute',
28205         '62',
28206         'Acirc',
28207         '63',
28208         'Atilde',
28209         '64',
28210         'Auml',
28211         '65',
28212         'Aring',
28213         '66',
28214         'AElig',
28215         '67',
28216         'Ccedil',
28217         '68',
28218         'Egrave',
28219         '69',
28220         'Eacute',
28221         '6a',
28222         'Ecirc',
28223         '6b',
28224         'Euml',
28225         '6c',
28226         'Igrave',
28227         '6d',
28228         'Iacute',
28229         '6e',
28230         'Icirc',
28231         '6f',
28232         'Iuml',
28233         '6g',
28234         'ETH',
28235         '6h',
28236         'Ntilde',
28237         '6i',
28238         'Ograve',
28239         '6j',
28240         'Oacute',
28241         '6k',
28242         'Ocirc',
28243         '6l',
28244         'Otilde',
28245         '6m',
28246         'Ouml',
28247         '6n',
28248         'times',
28249         '6o',
28250         'Oslash',
28251         '6p',
28252         'Ugrave',
28253         '6q',
28254         'Uacute',
28255         '6r',
28256         'Ucirc',
28257         '6s',
28258         'Uuml',
28259         '6t',
28260         'Yacute',
28261         '6u',
28262         'THORN',
28263         '6v',
28264         'szlig',
28265         '70',
28266         'agrave',
28267         '71',
28268         'aacute',
28269         '72',
28270         'acirc',
28271         '73',
28272         'atilde',
28273         '74',
28274         'auml',
28275         '75',
28276         'aring',
28277         '76',
28278         'aelig',
28279         '77',
28280         'ccedil',
28281         '78',
28282         'egrave',
28283         '79',
28284         'eacute',
28285         '7a',
28286         'ecirc',
28287         '7b',
28288         'euml',
28289         '7c',
28290         'igrave',
28291         '7d',
28292         'iacute',
28293         '7e',
28294         'icirc',
28295         '7f',
28296         'iuml',
28297         '7g',
28298         'eth',
28299         '7h',
28300         'ntilde',
28301         '7i',
28302         'ograve',
28303         '7j',
28304         'oacute',
28305         '7k',
28306         'ocirc',
28307         '7l',
28308         'otilde',
28309         '7m',
28310         'ouml',
28311         '7n',
28312         'divide',
28313         '7o',
28314         'oslash',
28315         '7p',
28316         'ugrave',
28317         '7q',
28318         'uacute',
28319         '7r',
28320         'ucirc',
28321         '7s',
28322         'uuml',
28323         '7t',
28324         'yacute',
28325         '7u',
28326         'thorn',
28327         '7v',
28328         'yuml',
28329         'ci',
28330         'fnof',
28331         'sh',
28332         'Alpha',
28333         'si',
28334         'Beta',
28335         'sj',
28336         'Gamma',
28337         'sk',
28338         'Delta',
28339         'sl',
28340         'Epsilon',
28341         'sm',
28342         'Zeta',
28343         'sn',
28344         'Eta',
28345         'so',
28346         'Theta',
28347         'sp',
28348         'Iota',
28349         'sq',
28350         'Kappa',
28351         'sr',
28352         'Lambda',
28353         'ss',
28354         'Mu',
28355         'st',
28356         'Nu',
28357         'su',
28358         'Xi',
28359         'sv',
28360         'Omicron',
28361         't0',
28362         'Pi',
28363         't1',
28364         'Rho',
28365         't3',
28366         'Sigma',
28367         't4',
28368         'Tau',
28369         't5',
28370         'Upsilon',
28371         't6',
28372         'Phi',
28373         't7',
28374         'Chi',
28375         't8',
28376         'Psi',
28377         't9',
28378         'Omega',
28379         'th',
28380         'alpha',
28381         'ti',
28382         'beta',
28383         'tj',
28384         'gamma',
28385         'tk',
28386         'delta',
28387         'tl',
28388         'epsilon',
28389         'tm',
28390         'zeta',
28391         'tn',
28392         'eta',
28393         'to',
28394         'theta',
28395         'tp',
28396         'iota',
28397         'tq',
28398         'kappa',
28399         'tr',
28400         'lambda',
28401         'ts',
28402         'mu',
28403         'tt',
28404         'nu',
28405         'tu',
28406         'xi',
28407         'tv',
28408         'omicron',
28409         'u0',
28410         'pi',
28411         'u1',
28412         'rho',
28413         'u2',
28414         'sigmaf',
28415         'u3',
28416         'sigma',
28417         'u4',
28418         'tau',
28419         'u5',
28420         'upsilon',
28421         'u6',
28422         'phi',
28423         'u7',
28424         'chi',
28425         'u8',
28426         'psi',
28427         'u9',
28428         'omega',
28429         'uh',
28430         'thetasym',
28431         'ui',
28432         'upsih',
28433         'um',
28434         'piv',
28435         '812',
28436         'bull',
28437         '816',
28438         'hellip',
28439         '81i',
28440         'prime',
28441         '81j',
28442         'Prime',
28443         '81u',
28444         'oline',
28445         '824',
28446         'frasl',
28447         '88o',
28448         'weierp',
28449         '88h',
28450         'image',
28451         '88s',
28452         'real',
28453         '892',
28454         'trade',
28455         '89l',
28456         'alefsym',
28457         '8cg',
28458         'larr',
28459         '8ch',
28460         'uarr',
28461         '8ci',
28462         'rarr',
28463         '8cj',
28464         'darr',
28465         '8ck',
28466         'harr',
28467         '8dl',
28468         'crarr',
28469         '8eg',
28470         'lArr',
28471         '8eh',
28472         'uArr',
28473         '8ei',
28474         'rArr',
28475         '8ej',
28476         'dArr',
28477         '8ek',
28478         'hArr',
28479         '8g0',
28480         'forall',
28481         '8g2',
28482         'part',
28483         '8g3',
28484         'exist',
28485         '8g5',
28486         'empty',
28487         '8g7',
28488         'nabla',
28489         '8g8',
28490         'isin',
28491         '8g9',
28492         'notin',
28493         '8gb',
28494         'ni',
28495         '8gf',
28496         'prod',
28497         '8gh',
28498         'sum',
28499         '8gi',
28500         'minus',
28501         '8gn',
28502         'lowast',
28503         '8gq',
28504         'radic',
28505         '8gt',
28506         'prop',
28507         '8gu',
28508         'infin',
28509         '8h0',
28510         'ang',
28511         '8h7',
28512         'and',
28513         '8h8',
28514         'or',
28515         '8h9',
28516         'cap',
28517         '8ha',
28518         'cup',
28519         '8hb',
28520         'int',
28521         '8hk',
28522         'there4',
28523         '8hs',
28524         'sim',
28525         '8i5',
28526         'cong',
28527         '8i8',
28528         'asymp',
28529         '8j0',
28530         'ne',
28531         '8j1',
28532         'equiv',
28533         '8j4',
28534         'le',
28535         '8j5',
28536         'ge',
28537         '8k2',
28538         'sub',
28539         '8k3',
28540         'sup',
28541         '8k4',
28542         'nsub',
28543         '8k6',
28544         'sube',
28545         '8k7',
28546         'supe',
28547         '8kl',
28548         'oplus',
28549         '8kn',
28550         'otimes',
28551         '8l5',
28552         'perp',
28553         '8m5',
28554         'sdot',
28555         '8o8',
28556         'lceil',
28557         '8o9',
28558         'rceil',
28559         '8oa',
28560         'lfloor',
28561         '8ob',
28562         'rfloor',
28563         '8p9',
28564         'lang',
28565         '8pa',
28566         'rang',
28567         '9ea',
28568         'loz',
28569         '9j0',
28570         'spades',
28571         '9j3',
28572         'clubs',
28573         '9j5',
28574         'hearts',
28575         '9j6',
28576         'diams',
28577         'ai',
28578         'OElig',
28579         'aj',
28580         'oelig',
28581         'b0',
28582         'Scaron',
28583         'b1',
28584         'scaron',
28585         'bo',
28586         'Yuml',
28587         'm6',
28588         'circ',
28589         'ms',
28590         'tilde',
28591         '802',
28592         'ensp',
28593         '803',
28594         'emsp',
28595         '809',
28596         'thinsp',
28597         '80c',
28598         'zwnj',
28599         '80d',
28600         'zwj',
28601         '80e',
28602         'lrm',
28603         '80f',
28604         'rlm',
28605         '80j',
28606         'ndash',
28607         '80k',
28608         'mdash',
28609         '80o',
28610         'lsquo',
28611         '80p',
28612         'rsquo',
28613         '80q',
28614         'sbquo',
28615         '80s',
28616         'ldquo',
28617         '80t',
28618         'rdquo',
28619         '80u',
28620         'bdquo',
28621         '810',
28622         'dagger',
28623         '811',
28624         'Dagger',
28625         '81g',
28626         'permil',
28627         '81p',
28628         'lsaquo',
28629         '81q',
28630         'rsaquo',
28631         '85c',
28632         'euro'
28633     ],
28634
28635          
28636     /**
28637      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28638      *
28639      * @method encodeRaw
28640      * @param {String} text Text to encode.
28641      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28642      * @return {String} Entity encoded text.
28643      */
28644     encodeRaw: function(text, attr)
28645     {
28646         var t = this;
28647         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28648             return t.baseEntities[chr] || chr;
28649         });
28650     },
28651     /**
28652      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28653      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28654      * and is exposed as the DOMUtils.encode function.
28655      *
28656      * @method encodeAllRaw
28657      * @param {String} text Text to encode.
28658      * @return {String} Entity encoded text.
28659      */
28660     encodeAllRaw: function(text) {
28661         var t = this;
28662         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28663             return t.baseEntities[chr] || chr;
28664         });
28665     },
28666     /**
28667      * Encodes the specified string using numeric entities. The core entities will be
28668      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28669      *
28670      * @method encodeNumeric
28671      * @param {String} text Text to encode.
28672      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28673      * @return {String} Entity encoded text.
28674      */
28675     encodeNumeric: function(text, attr) {
28676         var t = this;
28677         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28678             // Multi byte sequence convert it to a single entity
28679             if (chr.length > 1) {
28680                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28681             }
28682             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28683         });
28684     },
28685     /**
28686      * Encodes the specified string using named entities. The core entities will be encoded
28687      * as named ones but all non lower ascii characters will be encoded into named entities.
28688      *
28689      * @method encodeNamed
28690      * @param {String} text Text to encode.
28691      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28692      * @param {Object} entities Optional parameter with entities to use.
28693      * @return {String} Entity encoded text.
28694      */
28695     encodeNamed: function(text, attr, entities) {
28696         var t = this;
28697         entities = entities || this.namedEntities;
28698         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28699             return t.baseEntities[chr] || entities[chr] || chr;
28700         });
28701     },
28702     /**
28703      * Returns an encode function based on the name(s) and it's optional entities.
28704      *
28705      * @method getEncodeFunc
28706      * @param {String} name Comma separated list of encoders for example named,numeric.
28707      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28708      * @return {function} Encode function to be used.
28709      */
28710     getEncodeFunc: function(name, entities) {
28711         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28712         var t = this;
28713         function encodeNamedAndNumeric(text, attr) {
28714             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28715                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28716             });
28717         }
28718
28719         function encodeCustomNamed(text, attr) {
28720             return t.encodeNamed(text, attr, entities);
28721         }
28722         // Replace + with , to be compatible with previous TinyMCE versions
28723         name = this.makeMap(name.replace(/\+/g, ','));
28724         // Named and numeric encoder
28725         if (name.named && name.numeric) {
28726             return this.encodeNamedAndNumeric;
28727         }
28728         // Named encoder
28729         if (name.named) {
28730             // Custom names
28731             if (entities) {
28732                 return encodeCustomNamed;
28733             }
28734             return this.encodeNamed;
28735         }
28736         // Numeric
28737         if (name.numeric) {
28738             return this.encodeNumeric;
28739         }
28740         // Raw encoder
28741         return this.encodeRaw;
28742     },
28743     /**
28744      * Decodes the specified string, this will replace entities with raw UTF characters.
28745      *
28746      * @method decode
28747      * @param {String} text Text to entity decode.
28748      * @return {String} Entity decoded string.
28749      */
28750     decode: function(text)
28751     {
28752         var  t = this;
28753         return text.replace(this.entityRegExp, function(all, numeric) {
28754             if (numeric) {
28755                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28756                 // Support upper UTF
28757                 if (numeric > 65535) {
28758                     numeric -= 65536;
28759                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28760                 }
28761                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28762             }
28763             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28764         });
28765     },
28766     nativeDecode : function (text) {
28767         return text;
28768     },
28769     makeMap : function (items, delim, map) {
28770                 var i;
28771                 items = items || [];
28772                 delim = delim || ',';
28773                 if (typeof items == "string") {
28774                         items = items.split(delim);
28775                 }
28776                 map = map || {};
28777                 i = items.length;
28778                 while (i--) {
28779                         map[items[i]] = {};
28780                 }
28781                 return map;
28782         }
28783 };
28784     
28785     
28786     
28787 Roo.htmleditor.TidyEntities.init();
28788 /**
28789  * @class Roo.htmleditor.KeyEnter
28790  * Handle Enter press..
28791  * @cfg {Roo.HtmlEditorCore} core the editor.
28792  * @constructor
28793  * Create a new Filter.
28794  * @param {Object} config Configuration options
28795  */
28796
28797
28798
28799
28800
28801 Roo.htmleditor.KeyEnter = function(cfg) {
28802     Roo.apply(this, cfg);
28803     // this does not actually call walk as it's really just a abstract class
28804  
28805     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28806 }
28807
28808 //Roo.htmleditor.KeyEnter.i = 0;
28809
28810
28811 Roo.htmleditor.KeyEnter.prototype = {
28812     
28813     core : false,
28814     
28815     keypress : function(e)
28816     {
28817         if (e.charCode != 13 && e.charCode != 10) {
28818             Roo.log([e.charCode,e]);
28819             return true;
28820         }
28821         e.preventDefault();
28822         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28823         var doc = this.core.doc;
28824           //add a new line
28825        
28826     
28827         var sel = this.core.getSelection();
28828         var range = sel.getRangeAt(0);
28829         var n = range.commonAncestorContainer;
28830         var pc = range.closest([ 'ol', 'ul']);
28831         var pli = range.closest('li');
28832         if (!pc || e.ctrlKey) {
28833             // on it list, or ctrl pressed.
28834             if (!e.ctrlKey) {
28835                 sel.insertNode('br', 'after'); 
28836             } else {
28837                 // only do this if we have ctrl key..
28838                 var br = doc.createElement('br');
28839                 br.className = 'clear';
28840                 br.setAttribute('style', 'clear: both');
28841                 sel.insertNode(br, 'after'); 
28842             }
28843             
28844          
28845             this.core.undoManager.addEvent();
28846             this.core.fireEditorEvent(e);
28847             return false;
28848         }
28849         
28850         // deal with <li> insetion
28851         if (pli.innerText.trim() == '' &&
28852             pli.previousSibling &&
28853             pli.previousSibling.nodeName == 'LI' &&
28854             pli.previousSibling.innerText.trim() ==  '') {
28855             pli.parentNode.removeChild(pli.previousSibling);
28856             sel.cursorAfter(pc);
28857             this.core.undoManager.addEvent();
28858             this.core.fireEditorEvent(e);
28859             return false;
28860         }
28861     
28862         var li = doc.createElement('LI');
28863         li.innerHTML = '&nbsp;';
28864         if (!pli || !pli.firstSibling) {
28865             pc.appendChild(li);
28866         } else {
28867             pli.parentNode.insertBefore(li, pli.firstSibling);
28868         }
28869         sel.cursorText (li.firstChild);
28870       
28871         this.core.undoManager.addEvent();
28872         this.core.fireEditorEvent(e);
28873
28874         return false;
28875         
28876     
28877         
28878         
28879          
28880     }
28881 };
28882      
28883 /**
28884  * @class Roo.htmleditor.Block
28885  * Base class for html editor blocks - do not use it directly .. extend it..
28886  * @cfg {DomElement} node The node to apply stuff to.
28887  * @cfg {String} friendly_name the name that appears in the context bar about this block
28888  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28889  
28890  * @constructor
28891  * Create a new Filter.
28892  * @param {Object} config Configuration options
28893  */
28894
28895 Roo.htmleditor.Block  = function(cfg)
28896 {
28897     // do nothing .. should not be called really.
28898 }
28899 /**
28900  * factory method to get the block from an element (using cache if necessary)
28901  * @static
28902  * @param {HtmlElement} the dom element
28903  */
28904 Roo.htmleditor.Block.factory = function(node)
28905 {
28906     var cc = Roo.htmleditor.Block.cache;
28907     var id = Roo.get(node).id;
28908     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28909         Roo.htmleditor.Block.cache[id].readElement(node);
28910         return Roo.htmleditor.Block.cache[id];
28911     }
28912     var db  = node.getAttribute('data-block');
28913     if (!db) {
28914         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28915     }
28916     var cls = Roo.htmleditor['Block' + db];
28917     if (typeof(cls) == 'undefined') {
28918         //Roo.log(node.getAttribute('data-block'));
28919         Roo.log("OOps missing block : " + 'Block' + db);
28920         return false;
28921     }
28922     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28923     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28924 };
28925
28926 /**
28927  * initalize all Elements from content that are 'blockable'
28928  * @static
28929  * @param the body element
28930  */
28931 Roo.htmleditor.Block.initAll = function(body, type)
28932 {
28933     if (typeof(type) == 'undefined') {
28934         var ia = Roo.htmleditor.Block.initAll;
28935         ia(body,'table');
28936         ia(body,'td');
28937         ia(body,'figure');
28938         return;
28939     }
28940     Roo.each(Roo.get(body).query(type), function(e) {
28941         Roo.htmleditor.Block.factory(e);    
28942     },this);
28943 };
28944 // question goes here... do we need to clear out this cache sometimes?
28945 // or show we make it relivant to the htmleditor.
28946 Roo.htmleditor.Block.cache = {};
28947
28948 Roo.htmleditor.Block.prototype = {
28949     
28950     node : false,
28951     
28952      // used by context menu
28953     friendly_name : 'Based Block',
28954     
28955     // text for button to delete this element
28956     deleteTitle : false,
28957     
28958     context : false,
28959     /**
28960      * Update a node with values from this object
28961      * @param {DomElement} node
28962      */
28963     updateElement : function(node)
28964     {
28965         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28966     },
28967      /**
28968      * convert to plain HTML for calling insertAtCursor..
28969      */
28970     toHTML : function()
28971     {
28972         return Roo.DomHelper.markup(this.toObject());
28973     },
28974     /**
28975      * used by readEleemnt to extract data from a node
28976      * may need improving as it's pretty basic
28977      
28978      * @param {DomElement} node
28979      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28980      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28981      * @param {String} style the style property - eg. text-align
28982      */
28983     getVal : function(node, tag, attr, style)
28984     {
28985         var n = node;
28986         if (tag !== true && n.tagName != tag.toUpperCase()) {
28987             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28988             // but kiss for now.
28989             n = node.getElementsByTagName(tag).item(0);
28990         }
28991         if (!n) {
28992             return '';
28993         }
28994         if (attr === false) {
28995             return n;
28996         }
28997         if (attr == 'html') {
28998             return n.innerHTML;
28999         }
29000         if (attr == 'style') {
29001             return n.style[style]; 
29002         }
29003         
29004         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29005             
29006     },
29007     /**
29008      * create a DomHelper friendly object - for use with 
29009      * Roo.DomHelper.markup / overwrite / etc..
29010      * (override this)
29011      */
29012     toObject : function()
29013     {
29014         return {};
29015     },
29016       /**
29017      * Read a node that has a 'data-block' property - and extract the values from it.
29018      * @param {DomElement} node - the node
29019      */
29020     readElement : function(node)
29021     {
29022         
29023     } 
29024     
29025     
29026 };
29027
29028  
29029
29030 /**
29031  * @class Roo.htmleditor.BlockFigure
29032  * Block that has an image and a figcaption
29033  * @cfg {String} image_src the url for the image
29034  * @cfg {String} align (left|right) alignment for the block default left
29035  * @cfg {String} caption the text to appear below  (and in the alt tag)
29036  * @cfg {String} caption_display (block|none) display or not the caption
29037  * @cfg {String|number} image_width the width of the image number or %?
29038  * @cfg {String|number} image_height the height of the image number or %?
29039  * 
29040  * @constructor
29041  * Create a new Filter.
29042  * @param {Object} config Configuration options
29043  */
29044
29045 Roo.htmleditor.BlockFigure = function(cfg)
29046 {
29047     if (cfg.node) {
29048         this.readElement(cfg.node);
29049         this.updateElement(cfg.node);
29050     }
29051     Roo.apply(this, cfg);
29052 }
29053 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29054  
29055     
29056     // setable values.
29057     image_src: '',
29058     align: 'center',
29059     caption : '',
29060     caption_display : 'block',
29061     width : '100%',
29062     cls : '',
29063     href: '',
29064     video_url : '',
29065     
29066     // margin: '2%', not used
29067     
29068     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29069
29070     
29071     // used by context menu
29072     friendly_name : 'Image with caption',
29073     deleteTitle : "Delete Image and Caption",
29074     
29075     contextMenu : function(toolbar)
29076     {
29077         
29078         var block = function() {
29079             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29080         };
29081         
29082         
29083         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29084         
29085         var syncValue = toolbar.editorcore.syncValue;
29086         
29087         var fields = {};
29088         
29089         return [
29090              {
29091                 xtype : 'TextItem',
29092                 text : "Source: ",
29093                 xns : rooui.Toolbar  //Boostrap?
29094             },
29095             {
29096                 xtype : 'Button',
29097                 text: 'Change Image URL',
29098                  
29099                 listeners : {
29100                     click: function (btn, state)
29101                     {
29102                         var b = block();
29103                         
29104                         Roo.MessageBox.show({
29105                             title : "Image Source URL",
29106                             msg : "Enter the url for the image",
29107                             buttons: Roo.MessageBox.OKCANCEL,
29108                             fn: function(btn, val){
29109                                 if (btn != 'ok') {
29110                                     return;
29111                                 }
29112                                 b.image_src = val;
29113                                 b.updateElement();
29114                                 syncValue();
29115                                 toolbar.editorcore.onEditorEvent();
29116                             },
29117                             minWidth:250,
29118                             prompt:true,
29119                             //multiline: multiline,
29120                             modal : true,
29121                             value : b.image_src
29122                         });
29123                     }
29124                 },
29125                 xns : rooui.Toolbar
29126             },
29127          
29128             {
29129                 xtype : 'Button',
29130                 text: 'Change Link URL',
29131                  
29132                 listeners : {
29133                     click: function (btn, state)
29134                     {
29135                         var b = block();
29136                         
29137                         Roo.MessageBox.show({
29138                             title : "Link URL",
29139                             msg : "Enter the url for the link - leave blank to have no link",
29140                             buttons: Roo.MessageBox.OKCANCEL,
29141                             fn: function(btn, val){
29142                                 if (btn != 'ok') {
29143                                     return;
29144                                 }
29145                                 b.href = val;
29146                                 b.updateElement();
29147                                 syncValue();
29148                                 toolbar.editorcore.onEditorEvent();
29149                             },
29150                             minWidth:250,
29151                             prompt:true,
29152                             //multiline: multiline,
29153                             modal : true,
29154                             value : b.href
29155                         });
29156                     }
29157                 },
29158                 xns : rooui.Toolbar
29159             },
29160             {
29161                 xtype : 'Button',
29162                 text: 'Show Video URL',
29163                  
29164                 listeners : {
29165                     click: function (btn, state)
29166                     {
29167                         Roo.MessageBox.alert("Video URL",
29168                             block().video_url == '' ? 'This image is not linked ot a video' :
29169                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29170                     }
29171                 },
29172                 xns : rooui.Toolbar
29173             },
29174             
29175             
29176             {
29177                 xtype : 'TextItem',
29178                 text : "Width: ",
29179                 xns : rooui.Toolbar  //Boostrap?
29180             },
29181             {
29182                 xtype : 'ComboBox',
29183                 allowBlank : false,
29184                 displayField : 'val',
29185                 editable : true,
29186                 listWidth : 100,
29187                 triggerAction : 'all',
29188                 typeAhead : true,
29189                 valueField : 'val',
29190                 width : 70,
29191                 name : 'width',
29192                 listeners : {
29193                     select : function (combo, r, index)
29194                     {
29195                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29196                         var b = block();
29197                         b.width = r.get('val');
29198                         b.updateElement();
29199                         syncValue();
29200                         toolbar.editorcore.onEditorEvent();
29201                     }
29202                 },
29203                 xns : rooui.form,
29204                 store : {
29205                     xtype : 'SimpleStore',
29206                     data : [
29207                         ['100%'],
29208                         ['80%'],
29209                         ['50%'],
29210                         ['20%'],
29211                         ['10%']
29212                     ],
29213                     fields : [ 'val'],
29214                     xns : Roo.data
29215                 }
29216             },
29217             {
29218                 xtype : 'TextItem',
29219                 text : "Align: ",
29220                 xns : rooui.Toolbar  //Boostrap?
29221             },
29222             {
29223                 xtype : 'ComboBox',
29224                 allowBlank : false,
29225                 displayField : 'val',
29226                 editable : true,
29227                 listWidth : 100,
29228                 triggerAction : 'all',
29229                 typeAhead : true,
29230                 valueField : 'val',
29231                 width : 70,
29232                 name : 'align',
29233                 listeners : {
29234                     select : function (combo, r, index)
29235                     {
29236                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29237                         var b = block();
29238                         b.align = r.get('val');
29239                         b.updateElement();
29240                         syncValue();
29241                         toolbar.editorcore.onEditorEvent();
29242                     }
29243                 },
29244                 xns : rooui.form,
29245                 store : {
29246                     xtype : 'SimpleStore',
29247                     data : [
29248                         ['left'],
29249                         ['right'],
29250                         ['center']
29251                     ],
29252                     fields : [ 'val'],
29253                     xns : Roo.data
29254                 }
29255             },
29256             
29257             
29258             {
29259                 xtype : 'Button',
29260                 text: 'Hide Caption',
29261                 name : 'caption_display',
29262                 pressed : false,
29263                 enableToggle : true,
29264                 setValue : function(v) {
29265                     // this trigger toggle.
29266                      
29267                     this.setText(v ? "Hide Caption" : "Show Caption");
29268                     this.setPressed(v != 'block');
29269                 },
29270                 listeners : {
29271                     toggle: function (btn, state)
29272                     {
29273                         var b  = block();
29274                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29275                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29276                         b.updateElement();
29277                         syncValue();
29278                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29279                         toolbar.editorcore.onEditorEvent();
29280                     }
29281                 },
29282                 xns : rooui.Toolbar
29283             }
29284         ];
29285         
29286     },
29287     /**
29288      * create a DomHelper friendly object - for use with
29289      * Roo.DomHelper.markup / overwrite / etc..
29290      */
29291     toObject : function()
29292     {
29293         var d = document.createElement('div');
29294         d.innerHTML = this.caption;
29295         
29296         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29297         
29298         var iw = this.align == 'center' ? this.width : '100%';
29299         var img =   {
29300             tag : 'img',
29301             contenteditable : 'false',
29302             src : this.image_src,
29303             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29304             style: {
29305                 width : iw,
29306                 maxWidth : iw + ' !important', // this is not getting rendered?
29307                 margin : m  
29308                 
29309             }
29310         };
29311         /*
29312         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29313                     '<a href="{2}">' + 
29314                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29315                     '</a>' + 
29316                 '</div>',
29317         */
29318                 
29319         if (this.href.length > 0) {
29320             img = {
29321                 tag : 'a',
29322                 href: this.href,
29323                 contenteditable : 'true',
29324                 cn : [
29325                     img
29326                 ]
29327             };
29328         }
29329         
29330         
29331         if (this.video_url.length > 0) {
29332             img = {
29333                 tag : 'div',
29334                 cls : this.cls,
29335                 frameborder : 0,
29336                 allowfullscreen : true,
29337                 width : 420,  // these are for video tricks - that we replace the outer
29338                 height : 315,
29339                 src : this.video_url,
29340                 cn : [
29341                     img
29342                 ]
29343             };
29344         }
29345         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29346         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29347         
29348   
29349         var ret =   {
29350             tag: 'figure',
29351             'data-block' : 'Figure',
29352             'data-width' : this.width, 
29353             contenteditable : 'false',
29354             
29355             style : {
29356                 display: 'block',
29357                 float :  this.align ,
29358                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29359                 width : this.align == 'center' ? '100%' : this.width,
29360                 margin:  '0px',
29361                 padding: this.align == 'center' ? '0' : '0 10px' ,
29362                 textAlign : this.align   // seems to work for email..
29363                 
29364             },
29365            
29366             
29367             align : this.align,
29368             cn : [
29369                 img,
29370               
29371                 {
29372                     tag: 'figcaption',
29373                     'data-display' : this.caption_display,
29374                     style : {
29375                         textAlign : 'left',
29376                         fontSize : '16px',
29377                         lineHeight : '24px',
29378                         display : this.caption_display,
29379                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29380                         margin: m,
29381                         width: this.align == 'center' ?  this.width : '100%' 
29382                     
29383                          
29384                     },
29385                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29386                     cn : [
29387                         {
29388                             tag: 'div',
29389                             style  : {
29390                                 marginTop : '16px',
29391                                 textAlign : 'left'
29392                             },
29393                             align: 'left',
29394                             cn : [
29395                                 {
29396                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29397                                     tag : 'i',
29398                                     contenteditable : true,
29399                                     html : captionhtml
29400                                 }
29401                                 
29402                             ]
29403                         }
29404                         
29405                     ]
29406                     
29407                 }
29408             ]
29409         };
29410         return ret;
29411          
29412     },
29413     
29414     readElement : function(node)
29415     {
29416         // this should not really come from the link...
29417         this.video_url = this.getVal(node, 'div', 'src');
29418         this.cls = this.getVal(node, 'div', 'class');
29419         this.href = this.getVal(node, 'a', 'href');
29420         
29421         
29422         this.image_src = this.getVal(node, 'img', 'src');
29423          
29424         this.align = this.getVal(node, 'figure', 'align');
29425         var figcaption = this.getVal(node, 'figcaption', false);
29426         if (figcaption !== '') {
29427             this.caption = this.getVal(figcaption, 'i', 'html');
29428         }
29429         
29430
29431         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29432         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29433         this.width = this.getVal(node, true, 'data-width');
29434         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29435         
29436     },
29437     removeNode : function()
29438     {
29439         return this.node;
29440     }
29441     
29442   
29443    
29444      
29445     
29446     
29447     
29448     
29449 })
29450
29451  
29452
29453 /**
29454  * @class Roo.htmleditor.BlockTable
29455  * Block that manages a table
29456  * 
29457  * @constructor
29458  * Create a new Filter.
29459  * @param {Object} config Configuration options
29460  */
29461
29462 Roo.htmleditor.BlockTable = function(cfg)
29463 {
29464     if (cfg.node) {
29465         this.readElement(cfg.node);
29466         this.updateElement(cfg.node);
29467     }
29468     Roo.apply(this, cfg);
29469     if (!cfg.node) {
29470         this.rows = [];
29471         for(var r = 0; r < this.no_row; r++) {
29472             this.rows[r] = [];
29473             for(var c = 0; c < this.no_col; c++) {
29474                 this.rows[r][c] = this.emptyCell();
29475             }
29476         }
29477     }
29478     
29479     
29480 }
29481 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29482  
29483     rows : false,
29484     no_col : 1,
29485     no_row : 1,
29486     
29487     
29488     width: '100%',
29489     
29490     // used by context menu
29491     friendly_name : 'Table',
29492     deleteTitle : 'Delete Table',
29493     // context menu is drawn once..
29494     
29495     contextMenu : function(toolbar)
29496     {
29497         
29498         var block = function() {
29499             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29500         };
29501         
29502         
29503         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29504         
29505         var syncValue = toolbar.editorcore.syncValue;
29506         
29507         var fields = {};
29508         
29509         return [
29510             {
29511                 xtype : 'TextItem',
29512                 text : "Width: ",
29513                 xns : rooui.Toolbar  //Boostrap?
29514             },
29515             {
29516                 xtype : 'ComboBox',
29517                 allowBlank : false,
29518                 displayField : 'val',
29519                 editable : true,
29520                 listWidth : 100,
29521                 triggerAction : 'all',
29522                 typeAhead : true,
29523                 valueField : 'val',
29524                 width : 100,
29525                 name : 'width',
29526                 listeners : {
29527                     select : function (combo, r, index)
29528                     {
29529                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29530                         var b = block();
29531                         b.width = r.get('val');
29532                         b.updateElement();
29533                         syncValue();
29534                         toolbar.editorcore.onEditorEvent();
29535                     }
29536                 },
29537                 xns : rooui.form,
29538                 store : {
29539                     xtype : 'SimpleStore',
29540                     data : [
29541                         ['100%'],
29542                         ['auto']
29543                     ],
29544                     fields : [ 'val'],
29545                     xns : Roo.data
29546                 }
29547             },
29548             // -------- Cols
29549             
29550             {
29551                 xtype : 'TextItem',
29552                 text : "Columns: ",
29553                 xns : rooui.Toolbar  //Boostrap?
29554             },
29555          
29556             {
29557                 xtype : 'Button',
29558                 text: '-',
29559                 listeners : {
29560                     click : function (_self, e)
29561                     {
29562                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29563                         block().removeColumn();
29564                         syncValue();
29565                         toolbar.editorcore.onEditorEvent();
29566                     }
29567                 },
29568                 xns : rooui.Toolbar
29569             },
29570             {
29571                 xtype : 'Button',
29572                 text: '+',
29573                 listeners : {
29574                     click : function (_self, e)
29575                     {
29576                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29577                         block().addColumn();
29578                         syncValue();
29579                         toolbar.editorcore.onEditorEvent();
29580                     }
29581                 },
29582                 xns : rooui.Toolbar
29583             },
29584             // -------- ROWS
29585             {
29586                 xtype : 'TextItem',
29587                 text : "Rows: ",
29588                 xns : rooui.Toolbar  //Boostrap?
29589             },
29590          
29591             {
29592                 xtype : 'Button',
29593                 text: '-',
29594                 listeners : {
29595                     click : function (_self, e)
29596                     {
29597                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29598                         block().removeRow();
29599                         syncValue();
29600                         toolbar.editorcore.onEditorEvent();
29601                     }
29602                 },
29603                 xns : rooui.Toolbar
29604             },
29605             {
29606                 xtype : 'Button',
29607                 text: '+',
29608                 listeners : {
29609                     click : function (_self, e)
29610                     {
29611                         block().addRow();
29612                         syncValue();
29613                         toolbar.editorcore.onEditorEvent();
29614                     }
29615                 },
29616                 xns : rooui.Toolbar
29617             },
29618             // -------- ROWS
29619             {
29620                 xtype : 'Button',
29621                 text: 'Reset Column Widths',
29622                 listeners : {
29623                     
29624                     click : function (_self, e)
29625                     {
29626                         block().resetWidths();
29627                         syncValue();
29628                         toolbar.editorcore.onEditorEvent();
29629                     }
29630                 },
29631                 xns : rooui.Toolbar
29632             } 
29633             
29634             
29635             
29636         ];
29637         
29638     },
29639     
29640     
29641   /**
29642      * create a DomHelper friendly object - for use with
29643      * Roo.DomHelper.markup / overwrite / etc..
29644      * ?? should it be called with option to hide all editing features?
29645      */
29646     toObject : function()
29647     {
29648         
29649         var ret = {
29650             tag : 'table',
29651             contenteditable : 'false', // this stops cell selection from picking the table.
29652             'data-block' : 'Table',
29653             style : {
29654                 width:  this.width,
29655                 border : 'solid 1px #000', // ??? hard coded?
29656                 'border-collapse' : 'collapse' 
29657             },
29658             cn : [
29659                 { tag : 'tbody' , cn : [] }
29660             ]
29661         };
29662         
29663         // do we have a head = not really 
29664         var ncols = 0;
29665         Roo.each(this.rows, function( row ) {
29666             var tr = {
29667                 tag: 'tr',
29668                 style : {
29669                     margin: '6px',
29670                     border : 'solid 1px #000',
29671                     textAlign : 'left' 
29672                 },
29673                 cn : [ ]
29674             };
29675             
29676             ret.cn[0].cn.push(tr);
29677             // does the row have any properties? ?? height?
29678             var nc = 0;
29679             Roo.each(row, function( cell ) {
29680                 
29681                 var td = {
29682                     tag : 'td',
29683                     contenteditable :  'true',
29684                     'data-block' : 'Td',
29685                     html : cell.html,
29686                     style : cell.style
29687                 };
29688                 if (cell.colspan > 1) {
29689                     td.colspan = cell.colspan ;
29690                     nc += cell.colspan;
29691                 } else {
29692                     nc++;
29693                 }
29694                 if (cell.rowspan > 1) {
29695                     td.rowspan = cell.rowspan ;
29696                 }
29697                 
29698                 
29699                 // widths ?
29700                 tr.cn.push(td);
29701                     
29702                 
29703             }, this);
29704             ncols = Math.max(nc, ncols);
29705             
29706             
29707         }, this);
29708         // add the header row..
29709         
29710         ncols++;
29711          
29712         
29713         return ret;
29714          
29715     },
29716     
29717     readElement : function(node)
29718     {
29719         node  = node ? node : this.node ;
29720         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29721         
29722         this.rows = [];
29723         this.no_row = 0;
29724         var trs = Array.from(node.rows);
29725         trs.forEach(function(tr) {
29726             var row =  [];
29727             this.rows.push(row);
29728             
29729             this.no_row++;
29730             var no_column = 0;
29731             Array.from(tr.cells).forEach(function(td) {
29732                 
29733                 var add = {
29734                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29735                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29736                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29737                     html : td.innerHTML
29738                 };
29739                 no_column += add.colspan;
29740                      
29741                 
29742                 row.push(add);
29743                 
29744                 
29745             },this);
29746             this.no_col = Math.max(this.no_col, no_column);
29747             
29748             
29749         },this);
29750         
29751         
29752     },
29753     normalizeRows: function()
29754     {
29755         var ret= [];
29756         var rid = -1;
29757         this.rows.forEach(function(row) {
29758             rid++;
29759             ret[rid] = [];
29760             row = this.normalizeRow(row);
29761             var cid = 0;
29762             row.forEach(function(c) {
29763                 while (typeof(ret[rid][cid]) != 'undefined') {
29764                     cid++;
29765                 }
29766                 if (typeof(ret[rid]) == 'undefined') {
29767                     ret[rid] = [];
29768                 }
29769                 ret[rid][cid] = c;
29770                 c.row = rid;
29771                 c.col = cid;
29772                 if (c.rowspan < 2) {
29773                     return;
29774                 }
29775                 
29776                 for(var i = 1 ;i < c.rowspan; i++) {
29777                     if (typeof(ret[rid+i]) == 'undefined') {
29778                         ret[rid+i] = [];
29779                     }
29780                     ret[rid+i][cid] = c;
29781                 }
29782             });
29783         }, this);
29784         return ret;
29785     
29786     },
29787     
29788     normalizeRow: function(row)
29789     {
29790         var ret= [];
29791         row.forEach(function(c) {
29792             if (c.colspan < 2) {
29793                 ret.push(c);
29794                 return;
29795             }
29796             for(var i =0 ;i < c.colspan; i++) {
29797                 ret.push(c);
29798             }
29799         });
29800         return ret;
29801     
29802     },
29803     
29804     deleteColumn : function(sel)
29805     {
29806         if (!sel || sel.type != 'col') {
29807             return;
29808         }
29809         if (this.no_col < 2) {
29810             return;
29811         }
29812         
29813         this.rows.forEach(function(row) {
29814             var cols = this.normalizeRow(row);
29815             var col = cols[sel.col];
29816             if (col.colspan > 1) {
29817                 col.colspan --;
29818             } else {
29819                 row.remove(col);
29820             }
29821             
29822         }, this);
29823         this.no_col--;
29824         
29825     },
29826     removeColumn : function()
29827     {
29828         this.deleteColumn({
29829             type: 'col',
29830             col : this.no_col-1
29831         });
29832         this.updateElement();
29833     },
29834     
29835      
29836     addColumn : function()
29837     {
29838         
29839         this.rows.forEach(function(row) {
29840             row.push(this.emptyCell());
29841            
29842         }, this);
29843         this.updateElement();
29844     },
29845     
29846     deleteRow : function(sel)
29847     {
29848         if (!sel || sel.type != 'row') {
29849             return;
29850         }
29851         
29852         if (this.no_row < 2) {
29853             return;
29854         }
29855         
29856         var rows = this.normalizeRows();
29857         
29858         
29859         rows[sel.row].forEach(function(col) {
29860             if (col.rowspan > 1) {
29861                 col.rowspan--;
29862             } else {
29863                 col.remove = 1; // flage it as removed.
29864             }
29865             
29866         }, this);
29867         var newrows = [];
29868         this.rows.forEach(function(row) {
29869             newrow = [];
29870             row.forEach(function(c) {
29871                 if (typeof(c.remove) == 'undefined') {
29872                     newrow.push(c);
29873                 }
29874                 
29875             });
29876             if (newrow.length > 0) {
29877                 newrows.push(row);
29878             }
29879         });
29880         this.rows =  newrows;
29881         
29882         
29883         
29884         this.no_row--;
29885         this.updateElement();
29886         
29887     },
29888     removeRow : function()
29889     {
29890         this.deleteRow({
29891             type: 'row',
29892             row : this.no_row-1
29893         });
29894         
29895     },
29896     
29897      
29898     addRow : function()
29899     {
29900         
29901         var row = [];
29902         for (var i = 0; i < this.no_col; i++ ) {
29903             
29904             row.push(this.emptyCell());
29905            
29906         }
29907         this.rows.push(row);
29908         this.updateElement();
29909         
29910     },
29911      
29912     // the default cell object... at present...
29913     emptyCell : function() {
29914         return (new Roo.htmleditor.BlockTd({})).toObject();
29915         
29916      
29917     },
29918     
29919     removeNode : function()
29920     {
29921         return this.node;
29922     },
29923     
29924     
29925     
29926     resetWidths : function()
29927     {
29928         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29929             var nn = Roo.htmleditor.Block.factory(n);
29930             nn.width = '';
29931             nn.updateElement(n);
29932         });
29933     }
29934     
29935     
29936     
29937     
29938 })
29939
29940 /**
29941  *
29942  * editing a TD?
29943  *
29944  * since selections really work on the table cell, then editing really should work from there
29945  *
29946  * The original plan was to support merging etc... - but that may not be needed yet..
29947  *
29948  * So this simple version will support:
29949  *   add/remove cols
29950  *   adjust the width +/-
29951  *   reset the width...
29952  *   
29953  *
29954  */
29955
29956
29957  
29958
29959 /**
29960  * @class Roo.htmleditor.BlockTable
29961  * Block that manages a table
29962  * 
29963  * @constructor
29964  * Create a new Filter.
29965  * @param {Object} config Configuration options
29966  */
29967
29968 Roo.htmleditor.BlockTd = function(cfg)
29969 {
29970     if (cfg.node) {
29971         this.readElement(cfg.node);
29972         this.updateElement(cfg.node);
29973     }
29974     Roo.apply(this, cfg);
29975      
29976     
29977     
29978 }
29979 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29980  
29981     node : false,
29982     
29983     width: '',
29984     textAlign : 'left',
29985     valign : 'top',
29986     
29987     colspan : 1,
29988     rowspan : 1,
29989     
29990     
29991     // used by context menu
29992     friendly_name : 'Table Cell',
29993     deleteTitle : false, // use our customer delete
29994     
29995     // context menu is drawn once..
29996     
29997     contextMenu : function(toolbar)
29998     {
29999         
30000         var cell = function() {
30001             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30002         };
30003         
30004         var table = function() {
30005             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30006         };
30007         
30008         var lr = false;
30009         var saveSel = function()
30010         {
30011             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30012         }
30013         var restoreSel = function()
30014         {
30015             if (lr) {
30016                 (function() {
30017                     toolbar.editorcore.focus();
30018                     var cr = toolbar.editorcore.getSelection();
30019                     cr.removeAllRanges();
30020                     cr.addRange(lr);
30021                     toolbar.editorcore.onEditorEvent();
30022                 }).defer(10, this);
30023                 
30024                 
30025             }
30026         }
30027         
30028         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30029         
30030         var syncValue = toolbar.editorcore.syncValue;
30031         
30032         var fields = {};
30033         
30034         return [
30035             {
30036                 xtype : 'Button',
30037                 text : 'Edit Table',
30038                 listeners : {
30039                     click : function() {
30040                         var t = toolbar.tb.selectedNode.closest('table');
30041                         toolbar.editorcore.selectNode(t);
30042                         toolbar.editorcore.onEditorEvent();                        
30043                     }
30044                 }
30045                 
30046             },
30047               
30048            
30049              
30050             {
30051                 xtype : 'TextItem',
30052                 text : "Column Width: ",
30053                  xns : rooui.Toolbar 
30054                
30055             },
30056             {
30057                 xtype : 'Button',
30058                 text: '-',
30059                 listeners : {
30060                     click : function (_self, e)
30061                     {
30062                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30063                         cell().shrinkColumn();
30064                         syncValue();
30065                          toolbar.editorcore.onEditorEvent();
30066                     }
30067                 },
30068                 xns : rooui.Toolbar
30069             },
30070             {
30071                 xtype : 'Button',
30072                 text: '+',
30073                 listeners : {
30074                     click : function (_self, e)
30075                     {
30076                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30077                         cell().growColumn();
30078                         syncValue();
30079                         toolbar.editorcore.onEditorEvent();
30080                     }
30081                 },
30082                 xns : rooui.Toolbar
30083             },
30084             
30085             {
30086                 xtype : 'TextItem',
30087                 text : "Vertical Align: ",
30088                 xns : rooui.Toolbar  //Boostrap?
30089             },
30090             {
30091                 xtype : 'ComboBox',
30092                 allowBlank : false,
30093                 displayField : 'val',
30094                 editable : true,
30095                 listWidth : 100,
30096                 triggerAction : 'all',
30097                 typeAhead : true,
30098                 valueField : 'val',
30099                 width : 100,
30100                 name : 'valign',
30101                 listeners : {
30102                     select : function (combo, r, index)
30103                     {
30104                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30105                         var b = cell();
30106                         b.valign = r.get('val');
30107                         b.updateElement();
30108                         syncValue();
30109                         toolbar.editorcore.onEditorEvent();
30110                     }
30111                 },
30112                 xns : rooui.form,
30113                 store : {
30114                     xtype : 'SimpleStore',
30115                     data : [
30116                         ['top'],
30117                         ['middle'],
30118                         ['bottom'] // there are afew more... 
30119                     ],
30120                     fields : [ 'val'],
30121                     xns : Roo.data
30122                 }
30123             },
30124             
30125             {
30126                 xtype : 'TextItem',
30127                 text : "Merge Cells: ",
30128                  xns : rooui.Toolbar 
30129                
30130             },
30131             
30132             
30133             {
30134                 xtype : 'Button',
30135                 text: 'Right',
30136                 listeners : {
30137                     click : function (_self, e)
30138                     {
30139                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30140                         cell().mergeRight();
30141                         //block().growColumn();
30142                         syncValue();
30143                         toolbar.editorcore.onEditorEvent();
30144                     }
30145                 },
30146                 xns : rooui.Toolbar
30147             },
30148              
30149             {
30150                 xtype : 'Button',
30151                 text: 'Below',
30152                 listeners : {
30153                     click : function (_self, e)
30154                     {
30155                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30156                         cell().mergeBelow();
30157                         //block().growColumn();
30158                         syncValue();
30159                         toolbar.editorcore.onEditorEvent();
30160                     }
30161                 },
30162                 xns : rooui.Toolbar
30163             },
30164             {
30165                 xtype : 'TextItem',
30166                 text : "| ",
30167                  xns : rooui.Toolbar 
30168                
30169             },
30170             
30171             {
30172                 xtype : 'Button',
30173                 text: 'Split',
30174                 listeners : {
30175                     click : function (_self, e)
30176                     {
30177                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30178                         cell().split();
30179                         syncValue();
30180                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30181                         toolbar.editorcore.onEditorEvent();
30182                                              
30183                     }
30184                 },
30185                 xns : rooui.Toolbar
30186             },
30187             {
30188                 xtype : 'Fill',
30189                 xns : rooui.Toolbar 
30190                
30191             },
30192         
30193           
30194             {
30195                 xtype : 'Button',
30196                 text: 'Delete',
30197                  
30198                 xns : rooui.Toolbar,
30199                 menu : {
30200                     xtype : 'Menu',
30201                     xns : rooui.menu,
30202                     items : [
30203                         {
30204                             xtype : 'Item',
30205                             html: 'Column',
30206                             listeners : {
30207                                 click : function (_self, e)
30208                                 {
30209                                     var t = table();
30210                                     
30211                                     cell().deleteColumn();
30212                                     syncValue();
30213                                     toolbar.editorcore.selectNode(t.node);
30214                                     toolbar.editorcore.onEditorEvent();   
30215                                 }
30216                             },
30217                             xns : rooui.menu
30218                         },
30219                         {
30220                             xtype : 'Item',
30221                             html: 'Row',
30222                             listeners : {
30223                                 click : function (_self, e)
30224                                 {
30225                                     var t = table();
30226                                     cell().deleteRow();
30227                                     syncValue();
30228                                     
30229                                     toolbar.editorcore.selectNode(t.node);
30230                                     toolbar.editorcore.onEditorEvent();   
30231                                                          
30232                                 }
30233                             },
30234                             xns : rooui.menu
30235                         },
30236                        {
30237                             xtype : 'Separator',
30238                             xns : rooui.menu
30239                         },
30240                         {
30241                             xtype : 'Item',
30242                             html: 'Table',
30243                             listeners : {
30244                                 click : function (_self, e)
30245                                 {
30246                                     var t = table();
30247                                     var nn = t.node.nextSibling || t.node.previousSibling;
30248                                     t.node.parentNode.removeChild(t.node);
30249                                     if (nn) { 
30250                                         toolbar.editorcore.selectNode(nn, true);
30251                                     }
30252                                     toolbar.editorcore.onEditorEvent();   
30253                                                          
30254                                 }
30255                             },
30256                             xns : rooui.menu
30257                         }
30258                     ]
30259                 }
30260             }
30261             
30262             // align... << fixme
30263             
30264         ];
30265         
30266     },
30267     
30268     
30269   /**
30270      * create a DomHelper friendly object - for use with
30271      * Roo.DomHelper.markup / overwrite / etc..
30272      * ?? should it be called with option to hide all editing features?
30273      */
30274  /**
30275      * create a DomHelper friendly object - for use with
30276      * Roo.DomHelper.markup / overwrite / etc..
30277      * ?? should it be called with option to hide all editing features?
30278      */
30279     toObject : function()
30280     {
30281         var ret = {
30282             tag : 'td',
30283             contenteditable : 'true', // this stops cell selection from picking the table.
30284             'data-block' : 'Td',
30285             valign : this.valign,
30286             style : {  
30287                 'text-align' :  this.textAlign,
30288                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30289                 'border-collapse' : 'collapse',
30290                 padding : '6px', // 8 for desktop / 4 for mobile
30291                 'vertical-align': this.valign
30292             },
30293             html : this.html
30294         };
30295         if (this.width != '') {
30296             ret.width = this.width;
30297             ret.style.width = this.width;
30298         }
30299         
30300         
30301         if (this.colspan > 1) {
30302             ret.colspan = this.colspan ;
30303         } 
30304         if (this.rowspan > 1) {
30305             ret.rowspan = this.rowspan ;
30306         }
30307         
30308            
30309         
30310         return ret;
30311          
30312     },
30313     
30314     readElement : function(node)
30315     {
30316         node  = node ? node : this.node ;
30317         this.width = node.style.width;
30318         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30319         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30320         this.html = node.innerHTML;
30321         if (node.style.textAlign != '') {
30322             this.textAlign = node.style.textAlign;
30323         }
30324         
30325         
30326     },
30327      
30328     // the default cell object... at present...
30329     emptyCell : function() {
30330         return {
30331             colspan :  1,
30332             rowspan :  1,
30333             textAlign : 'left',
30334             html : "&nbsp;" // is this going to be editable now?
30335         };
30336      
30337     },
30338     
30339     removeNode : function()
30340     {
30341         return this.node.closest('table');
30342          
30343     },
30344     
30345     cellData : false,
30346     
30347     colWidths : false,
30348     
30349     toTableArray  : function()
30350     {
30351         var ret = [];
30352         var tab = this.node.closest('tr').closest('table');
30353         Array.from(tab.rows).forEach(function(r, ri){
30354             ret[ri] = [];
30355         });
30356         var rn = 0;
30357         this.colWidths = [];
30358         var all_auto = true;
30359         Array.from(tab.rows).forEach(function(r, ri){
30360             
30361             var cn = 0;
30362             Array.from(r.cells).forEach(function(ce, ci){
30363                 var c =  {
30364                     cell : ce,
30365                     row : rn,
30366                     col: cn,
30367                     colspan : ce.colSpan,
30368                     rowspan : ce.rowSpan
30369                 };
30370                 if (ce.isEqualNode(this.node)) {
30371                     this.cellData = c;
30372                 }
30373                 // if we have been filled up by a row?
30374                 if (typeof(ret[rn][cn]) != 'undefined') {
30375                     while(typeof(ret[rn][cn]) != 'undefined') {
30376                         cn++;
30377                     }
30378                     c.col = cn;
30379                 }
30380                 
30381                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30382                     this.colWidths[cn] =   ce.style.width;
30383                     if (this.colWidths[cn] != '') {
30384                         all_auto = false;
30385                     }
30386                 }
30387                 
30388                 
30389                 if (c.colspan < 2 && c.rowspan < 2 ) {
30390                     ret[rn][cn] = c;
30391                     cn++;
30392                     return;
30393                 }
30394                 for(var j = 0; j < c.rowspan; j++) {
30395                     if (typeof(ret[rn+j]) == 'undefined') {
30396                         continue; // we have a problem..
30397                     }
30398                     ret[rn+j][cn] = c;
30399                     for(var i = 0; i < c.colspan; i++) {
30400                         ret[rn+j][cn+i] = c;
30401                     }
30402                 }
30403                 
30404                 cn += c.colspan;
30405             }, this);
30406             rn++;
30407         }, this);
30408         
30409         // initalize widths.?
30410         // either all widths or no widths..
30411         if (all_auto) {
30412             this.colWidths[0] = false; // no widths flag.
30413         }
30414         
30415         
30416         return ret;
30417         
30418     },
30419     
30420     
30421     
30422     
30423     mergeRight: function()
30424     {
30425          
30426         // get the contents of the next cell along..
30427         var tr = this.node.closest('tr');
30428         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30429         if (i >= tr.childNodes.length - 1) {
30430             return; // no cells on right to merge with.
30431         }
30432         var table = this.toTableArray();
30433         
30434         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30435             return; // nothing right?
30436         }
30437         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30438         // right cell - must be same rowspan and on the same row.
30439         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30440             return; // right hand side is not same rowspan.
30441         }
30442         
30443         
30444         
30445         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30446         tr.removeChild(rc.cell);
30447         this.colspan += rc.colspan;
30448         this.node.setAttribute('colspan', this.colspan);
30449
30450         var table = this.toTableArray();
30451         this.normalizeWidths(table);
30452         this.updateWidths(table);
30453     },
30454     
30455     
30456     mergeBelow : function()
30457     {
30458         var table = this.toTableArray();
30459         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30460             return; // no row below
30461         }
30462         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30463             return; // nothing right?
30464         }
30465         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30466         
30467         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30468             return; // right hand side is not same rowspan.
30469         }
30470         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30471         rc.cell.parentNode.removeChild(rc.cell);
30472         this.rowspan += rc.rowspan;
30473         this.node.setAttribute('rowspan', this.rowspan);
30474     },
30475     
30476     split: function()
30477     {
30478         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30479             return;
30480         }
30481         var table = this.toTableArray();
30482         var cd = this.cellData;
30483         this.rowspan = 1;
30484         this.colspan = 1;
30485         
30486         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30487              
30488             
30489             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30490                 if (r == cd.row && c == cd.col) {
30491                     this.node.removeAttribute('rowspan');
30492                     this.node.removeAttribute('colspan');
30493                 }
30494                  
30495                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30496                 ntd.removeAttribute('id'); 
30497                 ntd.style.width  = this.colWidths[c];
30498                 ntd.innerHTML = '';
30499                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30500             }
30501             
30502         }
30503         this.redrawAllCells(table);
30504         
30505     },
30506     
30507     
30508     
30509     redrawAllCells: function(table)
30510     {
30511         
30512          
30513         var tab = this.node.closest('tr').closest('table');
30514         var ctr = tab.rows[0].parentNode;
30515         Array.from(tab.rows).forEach(function(r, ri){
30516             
30517             Array.from(r.cells).forEach(function(ce, ci){
30518                 ce.parentNode.removeChild(ce);
30519             });
30520             r.parentNode.removeChild(r);
30521         });
30522         for(var r = 0 ; r < table.length; r++) {
30523             var re = tab.rows[r];
30524             
30525             var re = tab.ownerDocument.createElement('tr');
30526             ctr.appendChild(re);
30527             for(var c = 0 ; c < table[r].length; c++) {
30528                 if (table[r][c].cell === false) {
30529                     continue;
30530                 }
30531                 
30532                 re.appendChild(table[r][c].cell);
30533                  
30534                 table[r][c].cell = false;
30535             }
30536         }
30537         
30538     },
30539     updateWidths : function(table)
30540     {
30541         for(var r = 0 ; r < table.length; r++) {
30542            
30543             for(var c = 0 ; c < table[r].length; c++) {
30544                 if (table[r][c].cell === false) {
30545                     continue;
30546                 }
30547                 
30548                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30549                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30550                     el.width = Math.floor(this.colWidths[c])  +'%';
30551                     el.updateElement(el.node);
30552                 }
30553                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30554                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30555                     var width = 0;
30556                     for(var i = 0; i < table[r][c].colspan; i ++) {
30557                         width += Math.floor(this.colWidths[c + i]);
30558                     }
30559                     el.width = width  +'%';
30560                     el.updateElement(el.node);
30561                 }
30562                 table[r][c].cell = false; // done
30563             }
30564         }
30565     },
30566     normalizeWidths : function(table)
30567     {
30568         if (this.colWidths[0] === false) {
30569             var nw = 100.0 / this.colWidths.length;
30570             this.colWidths.forEach(function(w,i) {
30571                 this.colWidths[i] = nw;
30572             },this);
30573             return;
30574         }
30575     
30576         var t = 0, missing = [];
30577         
30578         this.colWidths.forEach(function(w,i) {
30579             //if you mix % and
30580             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30581             var add =  this.colWidths[i];
30582             if (add > 0) {
30583                 t+=add;
30584                 return;
30585             }
30586             missing.push(i);
30587             
30588             
30589         },this);
30590         var nc = this.colWidths.length;
30591         if (missing.length) {
30592             var mult = (nc - missing.length) / (1.0 * nc);
30593             var t = mult * t;
30594             var ew = (100 -t) / (1.0 * missing.length);
30595             this.colWidths.forEach(function(w,i) {
30596                 if (w > 0) {
30597                     this.colWidths[i] = w * mult;
30598                     return;
30599                 }
30600                 
30601                 this.colWidths[i] = ew;
30602             }, this);
30603             // have to make up numbers..
30604              
30605         }
30606         // now we should have all the widths..
30607         
30608     
30609     },
30610     
30611     shrinkColumn : function()
30612     {
30613         var table = this.toTableArray();
30614         this.normalizeWidths(table);
30615         var col = this.cellData.col;
30616         var nw = this.colWidths[col] * 0.8;
30617         if (nw < 5) {
30618             return;
30619         }
30620         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30621         this.colWidths.forEach(function(w,i) {
30622             if (i == col) {
30623                  this.colWidths[i] = nw;
30624                 return;
30625             }
30626             this.colWidths[i] += otherAdd
30627         }, this);
30628         this.updateWidths(table);
30629          
30630     },
30631     growColumn : function()
30632     {
30633         var table = this.toTableArray();
30634         this.normalizeWidths(table);
30635         var col = this.cellData.col;
30636         var nw = this.colWidths[col] * 1.2;
30637         if (nw > 90) {
30638             return;
30639         }
30640         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30641         this.colWidths.forEach(function(w,i) {
30642             if (i == col) {
30643                 this.colWidths[i] = nw;
30644                 return;
30645             }
30646             this.colWidths[i] -= otherSub
30647         }, this);
30648         this.updateWidths(table);
30649          
30650     },
30651     deleteRow : function()
30652     {
30653         // delete this rows 'tr'
30654         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30655         // then reduce the rowspan.
30656         var table = this.toTableArray();
30657         // this.cellData.row;
30658         for (var i =0;i< table[this.cellData.row].length ; i++) {
30659             var c = table[this.cellData.row][i];
30660             if (c.row != this.cellData.row) {
30661                 
30662                 c.rowspan--;
30663                 c.cell.setAttribute('rowspan', c.rowspan);
30664                 continue;
30665             }
30666             if (c.rowspan > 1) {
30667                 c.rowspan--;
30668                 c.cell.setAttribute('rowspan', c.rowspan);
30669             }
30670         }
30671         table.splice(this.cellData.row,1);
30672         this.redrawAllCells(table);
30673         
30674     },
30675     deleteColumn : function()
30676     {
30677         var table = this.toTableArray();
30678         
30679         for (var i =0;i< table.length ; i++) {
30680             var c = table[i][this.cellData.col];
30681             if (c.col != this.cellData.col) {
30682                 table[i][this.cellData.col].colspan--;
30683             } else if (c.colspan > 1) {
30684                 c.colspan--;
30685                 c.cell.setAttribute('colspan', c.colspan);
30686             }
30687             table[i].splice(this.cellData.col,1);
30688         }
30689         
30690         this.redrawAllCells(table);
30691     }
30692     
30693     
30694     
30695     
30696 })
30697
30698 //<script type="text/javascript">
30699
30700 /*
30701  * Based  Ext JS Library 1.1.1
30702  * Copyright(c) 2006-2007, Ext JS, LLC.
30703  * LGPL
30704  *
30705  */
30706  
30707 /**
30708  * @class Roo.HtmlEditorCore
30709  * @extends Roo.Component
30710  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30711  *
30712  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30713  */
30714
30715 Roo.HtmlEditorCore = function(config){
30716     
30717     
30718     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30719     
30720     
30721     this.addEvents({
30722         /**
30723          * @event initialize
30724          * Fires when the editor is fully initialized (including the iframe)
30725          * @param {Roo.HtmlEditorCore} this
30726          */
30727         initialize: true,
30728         /**
30729          * @event activate
30730          * Fires when the editor is first receives the focus. Any insertion must wait
30731          * until after this event.
30732          * @param {Roo.HtmlEditorCore} this
30733          */
30734         activate: true,
30735          /**
30736          * @event beforesync
30737          * Fires before the textarea is updated with content from the editor iframe. Return false
30738          * to cancel the sync.
30739          * @param {Roo.HtmlEditorCore} this
30740          * @param {String} html
30741          */
30742         beforesync: true,
30743          /**
30744          * @event beforepush
30745          * Fires before the iframe editor is updated with content from the textarea. Return false
30746          * to cancel the push.
30747          * @param {Roo.HtmlEditorCore} this
30748          * @param {String} html
30749          */
30750         beforepush: true,
30751          /**
30752          * @event sync
30753          * Fires when the textarea is updated with content from the editor iframe.
30754          * @param {Roo.HtmlEditorCore} this
30755          * @param {String} html
30756          */
30757         sync: true,
30758          /**
30759          * @event push
30760          * Fires when the iframe editor is updated with content from the textarea.
30761          * @param {Roo.HtmlEditorCore} this
30762          * @param {String} html
30763          */
30764         push: true,
30765         
30766         /**
30767          * @event editorevent
30768          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30769          * @param {Roo.HtmlEditorCore} this
30770          */
30771         editorevent: true 
30772          
30773         
30774     });
30775     
30776     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30777     
30778     // defaults : white / black...
30779     this.applyBlacklists();
30780     
30781     
30782     
30783 };
30784
30785
30786 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30787
30788
30789      /**
30790      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30791      */
30792     
30793     owner : false,
30794     
30795      /**
30796      * @cfg {String} css styling for resizing. (used on bootstrap only)
30797      */
30798     resize : false,
30799      /**
30800      * @cfg {Number} height (in pixels)
30801      */   
30802     height: 300,
30803    /**
30804      * @cfg {Number} width (in pixels)
30805      */   
30806     width: 500,
30807      /**
30808      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30809      *         if you are doing an email editor, this probably needs disabling, it's designed
30810      */
30811     autoClean: true,
30812     
30813     /**
30814      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30815      */
30816     enableBlocks : true,
30817     /**
30818      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30819      * 
30820      */
30821     stylesheets: false,
30822      /**
30823      * @cfg {String} language default en - language of text (usefull for rtl languages)
30824      * 
30825      */
30826     language: 'en',
30827     
30828     /**
30829      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30830      *          - by default they are stripped - if you are editing email you may need this.
30831      */
30832     allowComments: false,
30833     // id of frame..
30834     frameId: false,
30835     
30836     // private properties
30837     validationEvent : false,
30838     deferHeight: true,
30839     initialized : false,
30840     activated : false,
30841     sourceEditMode : false,
30842     onFocus : Roo.emptyFn,
30843     iframePad:3,
30844     hideMode:'offsets',
30845     
30846     clearUp: true,
30847     
30848     // blacklist + whitelisted elements..
30849     black: false,
30850     white: false,
30851      
30852     bodyCls : '',
30853
30854     
30855     undoManager : false,
30856     /**
30857      * Protected method that will not generally be called directly. It
30858      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30859      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30860      */
30861     getDocMarkup : function(){
30862         // body styles..
30863         var st = '';
30864         
30865         // inherit styels from page...?? 
30866         if (this.stylesheets === false) {
30867             
30868             Roo.get(document.head).select('style').each(function(node) {
30869                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30870             });
30871             
30872             Roo.get(document.head).select('link').each(function(node) { 
30873                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30874             });
30875             
30876         } else if (!this.stylesheets.length) {
30877                 // simple..
30878                 st = '<style type="text/css">' +
30879                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30880                    '</style>';
30881         } else {
30882             for (var i in this.stylesheets) {
30883                 if (typeof(this.stylesheets[i]) != 'string') {
30884                     continue;
30885                 }
30886                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30887             }
30888             
30889         }
30890         
30891         st +=  '<style type="text/css">' +
30892             'IMG { cursor: pointer } ' +
30893         '</style>';
30894         
30895         st += '<meta name="google" content="notranslate">';
30896         
30897         var cls = 'notranslate roo-htmleditor-body';
30898         
30899         if(this.bodyCls.length){
30900             cls += ' ' + this.bodyCls;
30901         }
30902         
30903         return '<html  class="notranslate" translate="no"><head>' + st  +
30904             //<style type="text/css">' +
30905             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30906             //'</style>' +
30907             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30908     },
30909
30910     // private
30911     onRender : function(ct, position)
30912     {
30913         var _t = this;
30914         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30915         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30916         
30917         
30918         this.el.dom.style.border = '0 none';
30919         this.el.dom.setAttribute('tabIndex', -1);
30920         this.el.addClass('x-hidden hide');
30921         
30922         
30923         
30924         if(Roo.isIE){ // fix IE 1px bogus margin
30925             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30926         }
30927        
30928         
30929         this.frameId = Roo.id();
30930         
30931         var ifcfg = {
30932             tag: 'iframe',
30933             cls: 'form-control', // bootstrap..
30934             id: this.frameId,
30935             name: this.frameId,
30936             frameBorder : 'no',
30937             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30938         };
30939         if (this.resize) {
30940             ifcfg.style = { resize : this.resize };
30941         }
30942         
30943         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
30944         
30945         
30946         this.iframe = iframe.dom;
30947
30948         this.assignDocWin();
30949         
30950         this.doc.designMode = 'on';
30951        
30952         this.doc.open();
30953         this.doc.write(this.getDocMarkup());
30954         this.doc.close();
30955
30956         
30957         var task = { // must defer to wait for browser to be ready
30958             run : function(){
30959                 //console.log("run task?" + this.doc.readyState);
30960                 this.assignDocWin();
30961                 if(this.doc.body || this.doc.readyState == 'complete'){
30962                     try {
30963                         this.doc.designMode="on";
30964                         
30965                     } catch (e) {
30966                         return;
30967                     }
30968                     Roo.TaskMgr.stop(task);
30969                     this.initEditor.defer(10, this);
30970                 }
30971             },
30972             interval : 10,
30973             duration: 10000,
30974             scope: this
30975         };
30976         Roo.TaskMgr.start(task);
30977
30978     },
30979
30980     // private
30981     onResize : function(w, h)
30982     {
30983          Roo.log('resize: ' +w + ',' + h );
30984         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30985         if(!this.iframe){
30986             return;
30987         }
30988         if(typeof w == 'number'){
30989             
30990             this.iframe.style.width = w + 'px';
30991         }
30992         if(typeof h == 'number'){
30993             
30994             this.iframe.style.height = h + 'px';
30995             if(this.doc){
30996                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30997             }
30998         }
30999         
31000     },
31001
31002     /**
31003      * Toggles the editor between standard and source edit mode.
31004      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31005      */
31006     toggleSourceEdit : function(sourceEditMode){
31007         
31008         this.sourceEditMode = sourceEditMode === true;
31009         
31010         if(this.sourceEditMode){
31011  
31012             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31013             
31014         }else{
31015             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31016             //this.iframe.className = '';
31017             this.deferFocus();
31018         }
31019         //this.setSize(this.owner.wrap.getSize());
31020         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31021     },
31022
31023     
31024   
31025
31026     /**
31027      * Protected method that will not generally be called directly. If you need/want
31028      * custom HTML cleanup, this is the method you should override.
31029      * @param {String} html The HTML to be cleaned
31030      * return {String} The cleaned HTML
31031      */
31032     cleanHtml : function(html)
31033     {
31034         html = String(html);
31035         if(html.length > 5){
31036             if(Roo.isSafari){ // strip safari nonsense
31037                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31038             }
31039         }
31040         if(html == '&nbsp;'){
31041             html = '';
31042         }
31043         return html;
31044     },
31045
31046     /**
31047      * HTML Editor -> Textarea
31048      * Protected method that will not generally be called directly. Syncs the contents
31049      * of the editor iframe with the textarea.
31050      */
31051     syncValue : function()
31052     {
31053         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31054         if(this.initialized){
31055             
31056             if (this.undoManager) {
31057                 this.undoManager.addEvent();
31058             }
31059
31060             
31061             var bd = (this.doc.body || this.doc.documentElement);
31062            
31063             
31064             var sel = this.win.getSelection();
31065             
31066             var div = document.createElement('div');
31067             div.innerHTML = bd.innerHTML;
31068             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31069             if (gtx.length > 0) {
31070                 var rm = gtx.item(0).parentNode;
31071                 rm.parentNode.removeChild(rm);
31072             }
31073             
31074            
31075             if (this.enableBlocks) {
31076                 new Roo.htmleditor.FilterBlock({ node : div });
31077             }
31078             
31079             var html = div.innerHTML;
31080             
31081             //?? tidy?
31082             if (this.autoClean) {
31083                 
31084                 new Roo.htmleditor.FilterAttributes({
31085                     node : div,
31086                     attrib_white : [
31087                             'href',
31088                             'src',
31089                             'name',
31090                             'align',
31091                             'colspan',
31092                             'rowspan',
31093                             'data-display',
31094                             'data-width',
31095                             'start' ,
31096                             'style',
31097                             // youtube embed.
31098                             'class',
31099                             'allowfullscreen',
31100                             'frameborder',
31101                             'width',
31102                             'height',
31103                             'alt'
31104                             ],
31105                     attrib_clean : ['href', 'src' ] 
31106                 });
31107                 
31108                 var tidy = new Roo.htmleditor.TidySerializer({
31109                     inner:  true
31110                 });
31111                 html  = tidy.serialize(div);
31112                 
31113             }
31114             
31115             
31116             if(Roo.isSafari){
31117                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31118                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31119                 if(m && m[1]){
31120                     html = '<div style="'+m[0]+'">' + html + '</div>';
31121                 }
31122             }
31123             html = this.cleanHtml(html);
31124             // fix up the special chars.. normaly like back quotes in word...
31125             // however we do not want to do this with chinese..
31126             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31127                 
31128                 var cc = match.charCodeAt();
31129
31130                 // Get the character value, handling surrogate pairs
31131                 if (match.length == 2) {
31132                     // It's a surrogate pair, calculate the Unicode code point
31133                     var high = match.charCodeAt(0) - 0xD800;
31134                     var low  = match.charCodeAt(1) - 0xDC00;
31135                     cc = (high * 0x400) + low + 0x10000;
31136                 }  else if (
31137                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31138                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31139                     (cc >= 0xf900 && cc < 0xfb00 )
31140                 ) {
31141                         return match;
31142                 }  
31143          
31144                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31145                 return "&#" + cc + ";";
31146                 
31147                 
31148             });
31149             
31150             
31151              
31152             if(this.owner.fireEvent('beforesync', this, html) !== false){
31153                 this.el.dom.value = html;
31154                 this.owner.fireEvent('sync', this, html);
31155             }
31156         }
31157     },
31158
31159     /**
31160      * TEXTAREA -> EDITABLE
31161      * Protected method that will not generally be called directly. Pushes the value of the textarea
31162      * into the iframe editor.
31163      */
31164     pushValue : function()
31165     {
31166         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31167         if(this.initialized){
31168             var v = this.el.dom.value.trim();
31169             
31170             
31171             if(this.owner.fireEvent('beforepush', this, v) !== false){
31172                 var d = (this.doc.body || this.doc.documentElement);
31173                 d.innerHTML = v;
31174                  
31175                 this.el.dom.value = d.innerHTML;
31176                 this.owner.fireEvent('push', this, v);
31177             }
31178             if (this.autoClean) {
31179                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31180                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31181             }
31182             if (this.enableBlocks) {
31183                 Roo.htmleditor.Block.initAll(this.doc.body);
31184             }
31185             
31186             this.updateLanguage();
31187             
31188             var lc = this.doc.body.lastChild;
31189             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31190                 // add an extra line at the end.
31191                 this.doc.body.appendChild(this.doc.createElement('br'));
31192             }
31193             
31194             
31195         }
31196     },
31197
31198     // private
31199     deferFocus : function(){
31200         this.focus.defer(10, this);
31201     },
31202
31203     // doc'ed in Field
31204     focus : function(){
31205         if(this.win && !this.sourceEditMode){
31206             this.win.focus();
31207         }else{
31208             this.el.focus();
31209         }
31210     },
31211     
31212     assignDocWin: function()
31213     {
31214         var iframe = this.iframe;
31215         
31216          if(Roo.isIE){
31217             this.doc = iframe.contentWindow.document;
31218             this.win = iframe.contentWindow;
31219         } else {
31220 //            if (!Roo.get(this.frameId)) {
31221 //                return;
31222 //            }
31223 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31224 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31225             
31226             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31227                 return;
31228             }
31229             
31230             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31231             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31232         }
31233     },
31234     
31235     // private
31236     initEditor : function(){
31237         //console.log("INIT EDITOR");
31238         this.assignDocWin();
31239         
31240         
31241         
31242         this.doc.designMode="on";
31243         this.doc.open();
31244         this.doc.write(this.getDocMarkup());
31245         this.doc.close();
31246         
31247         var dbody = (this.doc.body || this.doc.documentElement);
31248         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31249         // this copies styles from the containing element into thsi one..
31250         // not sure why we need all of this..
31251         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31252         
31253         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31254         //ss['background-attachment'] = 'fixed'; // w3c
31255         dbody.bgProperties = 'fixed'; // ie
31256         dbody.setAttribute("translate", "no");
31257         
31258         //Roo.DomHelper.applyStyles(dbody, ss);
31259         Roo.EventManager.on(this.doc, {
31260              
31261             'mouseup': this.onEditorEvent,
31262             'dblclick': this.onEditorEvent,
31263             'click': this.onEditorEvent,
31264             'keyup': this.onEditorEvent,
31265             
31266             buffer:100,
31267             scope: this
31268         });
31269         Roo.EventManager.on(this.doc, {
31270             'paste': this.onPasteEvent,
31271             scope : this
31272         });
31273         if(Roo.isGecko){
31274             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31275         }
31276         //??? needed???
31277         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31278             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31279         }
31280         this.initialized = true;
31281
31282         
31283         // initialize special key events - enter
31284         new Roo.htmleditor.KeyEnter({core : this});
31285         
31286          
31287         
31288         this.owner.fireEvent('initialize', this);
31289         this.pushValue();
31290     },
31291     // this is to prevent a href clicks resulting in a redirect?
31292    
31293     onPasteEvent : function(e,v)
31294     {
31295         // I think we better assume paste is going to be a dirty load of rubish from word..
31296         
31297         // even pasting into a 'email version' of this widget will have to clean up that mess.
31298         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31299         
31300         // check what type of paste - if it's an image, then handle it differently.
31301         if (cd.files && cd.files.length > 0) {
31302             // pasting images?
31303             var urlAPI = (window.createObjectURL && window) || 
31304                 (window.URL && URL.revokeObjectURL && URL) || 
31305                 (window.webkitURL && webkitURL);
31306     
31307             var url = urlAPI.createObjectURL( cd.files[0]);
31308             this.insertAtCursor('<img src=" + url + ">');
31309             return false;
31310         }
31311         if (cd.types.indexOf('text/html') < 0 ) {
31312             return false;
31313         }
31314         var images = [];
31315         var html = cd.getData('text/html'); // clipboard event
31316         if (cd.types.indexOf('text/rtf') > -1) {
31317             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31318             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31319         }
31320         //Roo.log(images);
31321         //Roo.log(imgs);
31322         // fixme..
31323         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31324                        .map(function(g) { return g.toDataURL(); })
31325                        .filter(function(g) { return g != 'about:blank'; });
31326         
31327         //Roo.log(html);
31328         html = this.cleanWordChars(html);
31329         
31330         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31331         
31332         
31333         var sn = this.getParentElement();
31334         // check if d contains a table, and prevent nesting??
31335         //Roo.log(d.getElementsByTagName('table'));
31336         //Roo.log(sn);
31337         //Roo.log(sn.closest('table'));
31338         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31339             e.preventDefault();
31340             this.insertAtCursor("You can not nest tables");
31341             //Roo.log("prevent?"); // fixme - 
31342             return false;
31343         }
31344         
31345         
31346         
31347         if (images.length > 0) {
31348             // replace all v:imagedata - with img.
31349             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31350             Roo.each(ar, function(node) {
31351                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31352                 node.parentNode.removeChild(node);
31353             });
31354             
31355             
31356             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31357                 img.setAttribute('src', images[i]);
31358             });
31359         }
31360         if (this.autoClean) {
31361             new Roo.htmleditor.FilterWord({ node : d });
31362             
31363             new Roo.htmleditor.FilterStyleToTag({ node : d });
31364             new Roo.htmleditor.FilterAttributes({
31365                 node : d,
31366                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31367                 attrib_clean : ['href', 'src' ] 
31368             });
31369             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31370             // should be fonts..
31371             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31372             new Roo.htmleditor.FilterParagraph({ node : d });
31373             new Roo.htmleditor.FilterSpan({ node : d });
31374             new Roo.htmleditor.FilterLongBr({ node : d });
31375             new Roo.htmleditor.FilterComment({ node : d });
31376             
31377             
31378         }
31379         if (this.enableBlocks) {
31380                 
31381             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31382                 if (img.closest('figure')) { // assume!! that it's aready
31383                     return;
31384                 }
31385                 var fig  = new Roo.htmleditor.BlockFigure({
31386                     image_src  : img.src
31387                 });
31388                 fig.updateElement(img); // replace it..
31389                 
31390             });
31391         }
31392         
31393         
31394         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31395         if (this.enableBlocks) {
31396             Roo.htmleditor.Block.initAll(this.doc.body);
31397         }
31398          
31399         
31400         e.preventDefault();
31401         this.owner.fireEvent('paste', this);
31402         return false;
31403         // default behaveiour should be our local cleanup paste? (optional?)
31404         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31405         //this.owner.fireEvent('paste', e, v);
31406     },
31407     // private
31408     onDestroy : function(){
31409         
31410         
31411         
31412         if(this.rendered){
31413             
31414             //for (var i =0; i < this.toolbars.length;i++) {
31415             //    // fixme - ask toolbars for heights?
31416             //    this.toolbars[i].onDestroy();
31417            // }
31418             
31419             //this.wrap.dom.innerHTML = '';
31420             //this.wrap.remove();
31421         }
31422     },
31423
31424     // private
31425     onFirstFocus : function(){
31426         
31427         this.assignDocWin();
31428         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31429         
31430         this.activated = true;
31431          
31432     
31433         if(Roo.isGecko){ // prevent silly gecko errors
31434             this.win.focus();
31435             var s = this.win.getSelection();
31436             if(!s.focusNode || s.focusNode.nodeType != 3){
31437                 var r = s.getRangeAt(0);
31438                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31439                 r.collapse(true);
31440                 this.deferFocus();
31441             }
31442             try{
31443                 this.execCmd('useCSS', true);
31444                 this.execCmd('styleWithCSS', false);
31445             }catch(e){}
31446         }
31447         this.owner.fireEvent('activate', this);
31448     },
31449
31450     // private
31451     adjustFont: function(btn){
31452         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31453         //if(Roo.isSafari){ // safari
31454         //    adjust *= 2;
31455        // }
31456         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31457         if(Roo.isSafari){ // safari
31458             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31459             v =  (v < 10) ? 10 : v;
31460             v =  (v > 48) ? 48 : v;
31461             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31462             
31463         }
31464         
31465         
31466         v = Math.max(1, v+adjust);
31467         
31468         this.execCmd('FontSize', v  );
31469     },
31470
31471     onEditorEvent : function(e)
31472     {
31473          
31474         
31475         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31476             return; // we do not handle this.. (undo manager does..)
31477         }
31478         // in theory this detects if the last element is not a br, then we try and do that.
31479         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31480         if (e &&
31481             e.target.nodeName == 'BODY' &&
31482             e.type == "mouseup" &&
31483             this.doc.body.lastChild
31484            ) {
31485             var lc = this.doc.body.lastChild;
31486             // gtx-trans is google translate plugin adding crap.
31487             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31488                 lc = lc.previousSibling;
31489             }
31490             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31491             // if last element is <BR> - then dont do anything.
31492             
31493                 var ns = this.doc.createElement('br');
31494                 this.doc.body.appendChild(ns);
31495                 range = this.doc.createRange();
31496                 range.setStartAfter(ns);
31497                 range.collapse(true);
31498                 var sel = this.win.getSelection();
31499                 sel.removeAllRanges();
31500                 sel.addRange(range);
31501             }
31502         }
31503         
31504         
31505         
31506         this.fireEditorEvent(e);
31507       //  this.updateToolbar();
31508         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31509     },
31510     
31511     fireEditorEvent: function(e)
31512     {
31513         this.owner.fireEvent('editorevent', this, e);
31514     },
31515
31516     insertTag : function(tg)
31517     {
31518         // could be a bit smarter... -> wrap the current selected tRoo..
31519         if (tg.toLowerCase() == 'span' ||
31520             tg.toLowerCase() == 'code' ||
31521             tg.toLowerCase() == 'sup' ||
31522             tg.toLowerCase() == 'sub' 
31523             ) {
31524             
31525             range = this.createRange(this.getSelection());
31526             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31527             wrappingNode.appendChild(range.extractContents());
31528             range.insertNode(wrappingNode);
31529
31530             return;
31531             
31532             
31533             
31534         }
31535         this.execCmd("formatblock",   tg);
31536         this.undoManager.addEvent(); 
31537     },
31538     
31539     insertText : function(txt)
31540     {
31541         
31542         
31543         var range = this.createRange();
31544         range.deleteContents();
31545                //alert(Sender.getAttribute('label'));
31546                
31547         range.insertNode(this.doc.createTextNode(txt));
31548         this.undoManager.addEvent();
31549     } ,
31550     
31551      
31552
31553     /**
31554      * Executes a Midas editor command on the editor document and performs necessary focus and
31555      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31556      * @param {String} cmd The Midas command
31557      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31558      */
31559     relayCmd : function(cmd, value)
31560     {
31561         
31562         switch (cmd) {
31563             case 'justifyleft':
31564             case 'justifyright':
31565             case 'justifycenter':
31566                 // if we are in a cell, then we will adjust the
31567                 var n = this.getParentElement();
31568                 var td = n.closest('td');
31569                 if (td) {
31570                     var bl = Roo.htmleditor.Block.factory(td);
31571                     bl.textAlign = cmd.replace('justify','');
31572                     bl.updateElement();
31573                     this.owner.fireEvent('editorevent', this);
31574                     return;
31575                 }
31576                 this.execCmd('styleWithCSS', true); // 
31577                 break;
31578             case 'bold':
31579             case 'italic':
31580                 // if there is no selection, then we insert, and set the curson inside it..
31581                 this.execCmd('styleWithCSS', false); 
31582                 break;
31583                 
31584         
31585             default:
31586                 break;
31587         }
31588         
31589         
31590         this.win.focus();
31591         this.execCmd(cmd, value);
31592         this.owner.fireEvent('editorevent', this);
31593         //this.updateToolbar();
31594         this.owner.deferFocus();
31595     },
31596
31597     /**
31598      * Executes a Midas editor command directly on the editor document.
31599      * For visual commands, you should use {@link #relayCmd} instead.
31600      * <b>This should only be called after the editor is initialized.</b>
31601      * @param {String} cmd The Midas command
31602      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31603      */
31604     execCmd : function(cmd, value){
31605         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31606         this.syncValue();
31607     },
31608  
31609  
31610    
31611     /**
31612      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31613      * to insert tRoo.
31614      * @param {String} text | dom node.. 
31615      */
31616     insertAtCursor : function(text)
31617     {
31618         
31619         if(!this.activated){
31620             return;
31621         }
31622          
31623         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31624             this.win.focus();
31625             
31626             
31627             // from jquery ui (MIT licenced)
31628             var range, node;
31629             var win = this.win;
31630             
31631             if (win.getSelection && win.getSelection().getRangeAt) {
31632                 
31633                 // delete the existing?
31634                 
31635                 this.createRange(this.getSelection()).deleteContents();
31636                 range = win.getSelection().getRangeAt(0);
31637                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31638                 range.insertNode(node);
31639                 range = range.cloneRange();
31640                 range.collapse(false);
31641                  
31642                 win.getSelection().removeAllRanges();
31643                 win.getSelection().addRange(range);
31644                 
31645                 
31646                 
31647             } else if (win.document.selection && win.document.selection.createRange) {
31648                 // no firefox support
31649                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31650                 win.document.selection.createRange().pasteHTML(txt);
31651             
31652             } else {
31653                 // no firefox support
31654                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31655                 this.execCmd('InsertHTML', txt);
31656             } 
31657             this.syncValue();
31658             
31659             this.deferFocus();
31660         }
31661     },
31662  // private
31663     mozKeyPress : function(e){
31664         if(e.ctrlKey){
31665             var c = e.getCharCode(), cmd;
31666           
31667             if(c > 0){
31668                 c = String.fromCharCode(c).toLowerCase();
31669                 switch(c){
31670                     case 'b':
31671                         cmd = 'bold';
31672                         break;
31673                     case 'i':
31674                         cmd = 'italic';
31675                         break;
31676                     
31677                     case 'u':
31678                         cmd = 'underline';
31679                         break;
31680                     
31681                     //case 'v':
31682                       //  this.cleanUpPaste.defer(100, this);
31683                       //  return;
31684                         
31685                 }
31686                 if(cmd){
31687                     
31688                     this.relayCmd(cmd);
31689                     //this.win.focus();
31690                     //this.execCmd(cmd);
31691                     //this.deferFocus();
31692                     e.preventDefault();
31693                 }
31694                 
31695             }
31696         }
31697     },
31698
31699     // private
31700     fixKeys : function(){ // load time branching for fastest keydown performance
31701         
31702         
31703         if(Roo.isIE){
31704             return function(e){
31705                 var k = e.getKey(), r;
31706                 if(k == e.TAB){
31707                     e.stopEvent();
31708                     r = this.doc.selection.createRange();
31709                     if(r){
31710                         r.collapse(true);
31711                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31712                         this.deferFocus();
31713                     }
31714                     return;
31715                 }
31716                 /// this is handled by Roo.htmleditor.KeyEnter
31717                  /*
31718                 if(k == e.ENTER){
31719                     r = this.doc.selection.createRange();
31720                     if(r){
31721                         var target = r.parentElement();
31722                         if(!target || target.tagName.toLowerCase() != 'li'){
31723                             e.stopEvent();
31724                             r.pasteHTML('<br/>');
31725                             r.collapse(false);
31726                             r.select();
31727                         }
31728                     }
31729                 }
31730                 */
31731                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31732                 //    this.cleanUpPaste.defer(100, this);
31733                 //    return;
31734                 //}
31735                 
31736                 
31737             };
31738         }else if(Roo.isOpera){
31739             return function(e){
31740                 var k = e.getKey();
31741                 if(k == e.TAB){
31742                     e.stopEvent();
31743                     this.win.focus();
31744                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31745                     this.deferFocus();
31746                 }
31747                
31748                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31749                 //    this.cleanUpPaste.defer(100, this);
31750                  //   return;
31751                 //}
31752                 
31753             };
31754         }else if(Roo.isSafari){
31755             return function(e){
31756                 var k = e.getKey();
31757                 
31758                 if(k == e.TAB){
31759                     e.stopEvent();
31760                     this.execCmd('InsertText','\t');
31761                     this.deferFocus();
31762                     return;
31763                 }
31764                  this.mozKeyPress(e);
31765                 
31766                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31767                  //   this.cleanUpPaste.defer(100, this);
31768                  //   return;
31769                // }
31770                 
31771              };
31772         }
31773     }(),
31774     
31775     getAllAncestors: function()
31776     {
31777         var p = this.getSelectedNode();
31778         var a = [];
31779         if (!p) {
31780             a.push(p); // push blank onto stack..
31781             p = this.getParentElement();
31782         }
31783         
31784         
31785         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31786             a.push(p);
31787             p = p.parentNode;
31788         }
31789         a.push(this.doc.body);
31790         return a;
31791     },
31792     lastSel : false,
31793     lastSelNode : false,
31794     
31795     
31796     getSelection : function() 
31797     {
31798         this.assignDocWin();
31799         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31800     },
31801     /**
31802      * Select a dom node
31803      * @param {DomElement} node the node to select
31804      */
31805     selectNode : function(node, collapse)
31806     {
31807         var nodeRange = node.ownerDocument.createRange();
31808         try {
31809             nodeRange.selectNode(node);
31810         } catch (e) {
31811             nodeRange.selectNodeContents(node);
31812         }
31813         if (collapse === true) {
31814             nodeRange.collapse(true);
31815         }
31816         //
31817         var s = this.win.getSelection();
31818         s.removeAllRanges();
31819         s.addRange(nodeRange);
31820     },
31821     
31822     getSelectedNode: function() 
31823     {
31824         // this may only work on Gecko!!!
31825         
31826         // should we cache this!!!!
31827         
31828          
31829          
31830         var range = this.createRange(this.getSelection()).cloneRange();
31831         
31832         if (Roo.isIE) {
31833             var parent = range.parentElement();
31834             while (true) {
31835                 var testRange = range.duplicate();
31836                 testRange.moveToElementText(parent);
31837                 if (testRange.inRange(range)) {
31838                     break;
31839                 }
31840                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31841                     break;
31842                 }
31843                 parent = parent.parentElement;
31844             }
31845             return parent;
31846         }
31847         
31848         // is ancestor a text element.
31849         var ac =  range.commonAncestorContainer;
31850         if (ac.nodeType == 3) {
31851             ac = ac.parentNode;
31852         }
31853         
31854         var ar = ac.childNodes;
31855          
31856         var nodes = [];
31857         var other_nodes = [];
31858         var has_other_nodes = false;
31859         for (var i=0;i<ar.length;i++) {
31860             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31861                 continue;
31862             }
31863             // fullly contained node.
31864             
31865             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31866                 nodes.push(ar[i]);
31867                 continue;
31868             }
31869             
31870             // probably selected..
31871             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31872                 other_nodes.push(ar[i]);
31873                 continue;
31874             }
31875             // outer..
31876             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31877                 continue;
31878             }
31879             
31880             
31881             has_other_nodes = true;
31882         }
31883         if (!nodes.length && other_nodes.length) {
31884             nodes= other_nodes;
31885         }
31886         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31887             return false;
31888         }
31889         
31890         return nodes[0];
31891     },
31892     
31893     
31894     createRange: function(sel)
31895     {
31896         // this has strange effects when using with 
31897         // top toolbar - not sure if it's a great idea.
31898         //this.editor.contentWindow.focus();
31899         if (typeof sel != "undefined") {
31900             try {
31901                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31902             } catch(e) {
31903                 return this.doc.createRange();
31904             }
31905         } else {
31906             return this.doc.createRange();
31907         }
31908     },
31909     getParentElement: function()
31910     {
31911         
31912         this.assignDocWin();
31913         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31914         
31915         var range = this.createRange(sel);
31916          
31917         try {
31918             var p = range.commonAncestorContainer;
31919             while (p.nodeType == 3) { // text node
31920                 p = p.parentNode;
31921             }
31922             return p;
31923         } catch (e) {
31924             return null;
31925         }
31926     
31927     },
31928     /***
31929      *
31930      * Range intersection.. the hard stuff...
31931      *  '-1' = before
31932      *  '0' = hits..
31933      *  '1' = after.
31934      *         [ -- selected range --- ]
31935      *   [fail]                        [fail]
31936      *
31937      *    basically..
31938      *      if end is before start or  hits it. fail.
31939      *      if start is after end or hits it fail.
31940      *
31941      *   if either hits (but other is outside. - then it's not 
31942      *   
31943      *    
31944      **/
31945     
31946     
31947     // @see http://www.thismuchiknow.co.uk/?p=64.
31948     rangeIntersectsNode : function(range, node)
31949     {
31950         var nodeRange = node.ownerDocument.createRange();
31951         try {
31952             nodeRange.selectNode(node);
31953         } catch (e) {
31954             nodeRange.selectNodeContents(node);
31955         }
31956     
31957         var rangeStartRange = range.cloneRange();
31958         rangeStartRange.collapse(true);
31959     
31960         var rangeEndRange = range.cloneRange();
31961         rangeEndRange.collapse(false);
31962     
31963         var nodeStartRange = nodeRange.cloneRange();
31964         nodeStartRange.collapse(true);
31965     
31966         var nodeEndRange = nodeRange.cloneRange();
31967         nodeEndRange.collapse(false);
31968     
31969         return rangeStartRange.compareBoundaryPoints(
31970                  Range.START_TO_START, nodeEndRange) == -1 &&
31971                rangeEndRange.compareBoundaryPoints(
31972                  Range.START_TO_START, nodeStartRange) == 1;
31973         
31974          
31975     },
31976     rangeCompareNode : function(range, node)
31977     {
31978         var nodeRange = node.ownerDocument.createRange();
31979         try {
31980             nodeRange.selectNode(node);
31981         } catch (e) {
31982             nodeRange.selectNodeContents(node);
31983         }
31984         
31985         
31986         range.collapse(true);
31987     
31988         nodeRange.collapse(true);
31989      
31990         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31991         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31992          
31993         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31994         
31995         var nodeIsBefore   =  ss == 1;
31996         var nodeIsAfter    = ee == -1;
31997         
31998         if (nodeIsBefore && nodeIsAfter) {
31999             return 0; // outer
32000         }
32001         if (!nodeIsBefore && nodeIsAfter) {
32002             return 1; //right trailed.
32003         }
32004         
32005         if (nodeIsBefore && !nodeIsAfter) {
32006             return 2;  // left trailed.
32007         }
32008         // fully contined.
32009         return 3;
32010     },
32011  
32012     cleanWordChars : function(input) {// change the chars to hex code
32013         
32014        var swapCodes  = [ 
32015             [    8211, "&#8211;" ], 
32016             [    8212, "&#8212;" ], 
32017             [    8216,  "'" ],  
32018             [    8217, "'" ],  
32019             [    8220, '"' ],  
32020             [    8221, '"' ],  
32021             [    8226, "*" ],  
32022             [    8230, "..." ]
32023         ]; 
32024         var output = input;
32025         Roo.each(swapCodes, function(sw) { 
32026             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32027             
32028             output = output.replace(swapper, sw[1]);
32029         });
32030         
32031         return output;
32032     },
32033     
32034      
32035     
32036         
32037     
32038     cleanUpChild : function (node)
32039     {
32040         
32041         new Roo.htmleditor.FilterComment({node : node});
32042         new Roo.htmleditor.FilterAttributes({
32043                 node : node,
32044                 attrib_black : this.ablack,
32045                 attrib_clean : this.aclean,
32046                 style_white : this.cwhite,
32047                 style_black : this.cblack
32048         });
32049         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32050         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32051          
32052         
32053     },
32054     
32055     /**
32056      * Clean up MS wordisms...
32057      * @deprecated - use filter directly
32058      */
32059     cleanWord : function(node)
32060     {
32061         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32062         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32063         
32064     },
32065    
32066     
32067     /**
32068
32069      * @deprecated - use filters
32070      */
32071     cleanTableWidths : function(node)
32072     {
32073         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32074         
32075  
32076     },
32077     
32078      
32079         
32080     applyBlacklists : function()
32081     {
32082         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32083         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32084         
32085         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32086         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32087         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32088         
32089         this.white = [];
32090         this.black = [];
32091         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32092             if (b.indexOf(tag) > -1) {
32093                 return;
32094             }
32095             this.white.push(tag);
32096             
32097         }, this);
32098         
32099         Roo.each(w, function(tag) {
32100             if (b.indexOf(tag) > -1) {
32101                 return;
32102             }
32103             if (this.white.indexOf(tag) > -1) {
32104                 return;
32105             }
32106             this.white.push(tag);
32107             
32108         }, this);
32109         
32110         
32111         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32112             if (w.indexOf(tag) > -1) {
32113                 return;
32114             }
32115             this.black.push(tag);
32116             
32117         }, this);
32118         
32119         Roo.each(b, function(tag) {
32120             if (w.indexOf(tag) > -1) {
32121                 return;
32122             }
32123             if (this.black.indexOf(tag) > -1) {
32124                 return;
32125             }
32126             this.black.push(tag);
32127             
32128         }, this);
32129         
32130         
32131         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32132         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32133         
32134         this.cwhite = [];
32135         this.cblack = [];
32136         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32137             if (b.indexOf(tag) > -1) {
32138                 return;
32139             }
32140             this.cwhite.push(tag);
32141             
32142         }, this);
32143         
32144         Roo.each(w, function(tag) {
32145             if (b.indexOf(tag) > -1) {
32146                 return;
32147             }
32148             if (this.cwhite.indexOf(tag) > -1) {
32149                 return;
32150             }
32151             this.cwhite.push(tag);
32152             
32153         }, this);
32154         
32155         
32156         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32157             if (w.indexOf(tag) > -1) {
32158                 return;
32159             }
32160             this.cblack.push(tag);
32161             
32162         }, this);
32163         
32164         Roo.each(b, function(tag) {
32165             if (w.indexOf(tag) > -1) {
32166                 return;
32167             }
32168             if (this.cblack.indexOf(tag) > -1) {
32169                 return;
32170             }
32171             this.cblack.push(tag);
32172             
32173         }, this);
32174     },
32175     
32176     setStylesheets : function(stylesheets)
32177     {
32178         if(typeof(stylesheets) == 'string'){
32179             Roo.get(this.iframe.contentDocument.head).createChild({
32180                 tag : 'link',
32181                 rel : 'stylesheet',
32182                 type : 'text/css',
32183                 href : stylesheets
32184             });
32185             
32186             return;
32187         }
32188         var _this = this;
32189      
32190         Roo.each(stylesheets, function(s) {
32191             if(!s.length){
32192                 return;
32193             }
32194             
32195             Roo.get(_this.iframe.contentDocument.head).createChild({
32196                 tag : 'link',
32197                 rel : 'stylesheet',
32198                 type : 'text/css',
32199                 href : s
32200             });
32201         });
32202
32203         
32204     },
32205     
32206     
32207     updateLanguage : function()
32208     {
32209         if (!this.iframe || !this.iframe.contentDocument) {
32210             return;
32211         }
32212         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32213     },
32214     
32215     
32216     removeStylesheets : function()
32217     {
32218         var _this = this;
32219         
32220         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32221             s.remove();
32222         });
32223     },
32224     
32225     setStyle : function(style)
32226     {
32227         Roo.get(this.iframe.contentDocument.head).createChild({
32228             tag : 'style',
32229             type : 'text/css',
32230             html : style
32231         });
32232
32233         return;
32234     }
32235     
32236     // hide stuff that is not compatible
32237     /**
32238      * @event blur
32239      * @hide
32240      */
32241     /**
32242      * @event change
32243      * @hide
32244      */
32245     /**
32246      * @event focus
32247      * @hide
32248      */
32249     /**
32250      * @event specialkey
32251      * @hide
32252      */
32253     /**
32254      * @cfg {String} fieldClass @hide
32255      */
32256     /**
32257      * @cfg {String} focusClass @hide
32258      */
32259     /**
32260      * @cfg {String} autoCreate @hide
32261      */
32262     /**
32263      * @cfg {String} inputType @hide
32264      */
32265     /**
32266      * @cfg {String} invalidClass @hide
32267      */
32268     /**
32269      * @cfg {String} invalidText @hide
32270      */
32271     /**
32272      * @cfg {String} msgFx @hide
32273      */
32274     /**
32275      * @cfg {String} validateOnBlur @hide
32276      */
32277 });
32278
32279 Roo.HtmlEditorCore.white = [
32280         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32281         
32282        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32283        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32284        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32285        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32286        'TABLE',   'UL',         'XMP', 
32287        
32288        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32289       'THEAD',   'TR', 
32290      
32291       'DIR', 'MENU', 'OL', 'UL', 'DL',
32292        
32293       'EMBED',  'OBJECT'
32294 ];
32295
32296
32297 Roo.HtmlEditorCore.black = [
32298     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32299         'APPLET', // 
32300         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32301         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32302         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32303         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32304         //'FONT' // CLEAN LATER..
32305         'COLGROUP', 'COL'   // messy tables.
32306         
32307         
32308 ];
32309 Roo.HtmlEditorCore.clean = [ // ?? needed???
32310      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32311 ];
32312 Roo.HtmlEditorCore.tag_remove = [
32313     'FONT', 'TBODY'  
32314 ];
32315 // attributes..
32316
32317 Roo.HtmlEditorCore.ablack = [
32318     'on'
32319 ];
32320     
32321 Roo.HtmlEditorCore.aclean = [ 
32322     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32323 ];
32324
32325 // protocols..
32326 Roo.HtmlEditorCore.pwhite= [
32327         'http',  'https',  'mailto'
32328 ];
32329
32330 // white listed style attributes.
32331 Roo.HtmlEditorCore.cwhite= [
32332       //  'text-align', /// default is to allow most things..
32333       
32334          
32335 //        'font-size'//??
32336 ];
32337
32338 // black listed style attributes.
32339 Roo.HtmlEditorCore.cblack= [
32340       //  'font-size' -- this can be set by the project 
32341 ];
32342
32343
32344
32345
32346     /*
32347  * - LGPL
32348  *
32349  * HtmlEditor
32350  * 
32351  */
32352
32353 /**
32354  * @class Roo.bootstrap.form.HtmlEditor
32355  * @extends Roo.bootstrap.form.TextArea
32356  * Bootstrap HtmlEditor class
32357
32358  * @constructor
32359  * Create a new HtmlEditor
32360  * @param {Object} config The config object
32361  */
32362
32363 Roo.bootstrap.form.HtmlEditor = function(config){
32364     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32365     if (!this.toolbars) {
32366         this.toolbars = [];
32367     }
32368     
32369     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32370     this.addEvents({
32371             /**
32372              * @event initialize
32373              * Fires when the editor is fully initialized (including the iframe)
32374              * @param {HtmlEditor} this
32375              */
32376             initialize: true,
32377             /**
32378              * @event activate
32379              * Fires when the editor is first receives the focus. Any insertion must wait
32380              * until after this event.
32381              * @param {HtmlEditor} this
32382              */
32383             activate: true,
32384              /**
32385              * @event beforesync
32386              * Fires before the textarea is updated with content from the editor iframe. Return false
32387              * to cancel the sync.
32388              * @param {HtmlEditor} this
32389              * @param {String} html
32390              */
32391             beforesync: true,
32392              /**
32393              * @event beforepush
32394              * Fires before the iframe editor is updated with content from the textarea. Return false
32395              * to cancel the push.
32396              * @param {HtmlEditor} this
32397              * @param {String} html
32398              */
32399             beforepush: true,
32400              /**
32401              * @event sync
32402              * Fires when the textarea is updated with content from the editor iframe.
32403              * @param {HtmlEditor} this
32404              * @param {String} html
32405              */
32406             sync: true,
32407              /**
32408              * @event push
32409              * Fires when the iframe editor is updated with content from the textarea.
32410              * @param {HtmlEditor} this
32411              * @param {String} html
32412              */
32413             push: true,
32414              /**
32415              * @event editmodechange
32416              * Fires when the editor switches edit modes
32417              * @param {HtmlEditor} this
32418              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32419              */
32420             editmodechange: true,
32421             /**
32422              * @event editorevent
32423              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32424              * @param {HtmlEditor} this
32425              */
32426             editorevent: true,
32427             /**
32428              * @event firstfocus
32429              * Fires when on first focus - needed by toolbars..
32430              * @param {HtmlEditor} this
32431              */
32432             firstfocus: true,
32433             /**
32434              * @event autosave
32435              * Auto save the htmlEditor value as a file into Events
32436              * @param {HtmlEditor} this
32437              */
32438             autosave: true,
32439             /**
32440              * @event savedpreview
32441              * preview the saved version of htmlEditor
32442              * @param {HtmlEditor} this
32443              */
32444             savedpreview: true
32445         });
32446 };
32447
32448
32449 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32450     
32451     
32452       /**
32453      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32454      */
32455     toolbars : false,
32456     
32457      /**
32458     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32459     */
32460     btns : [],
32461    
32462      /**
32463      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32464      */
32465     resize : false,
32466      /**
32467      * @cfg {Number} height (in pixels)
32468      */   
32469     height: 300,
32470    /**
32471      * @cfg {Number} width (in pixels)
32472      */   
32473     width: false,
32474     
32475     /**
32476      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32477      * 
32478      */
32479     stylesheets: false,
32480     
32481     // id of frame..
32482     frameId: false,
32483     
32484     // private properties
32485     validationEvent : false,
32486     deferHeight: true,
32487     initialized : false,
32488     activated : false,
32489     
32490     onFocus : Roo.emptyFn,
32491     iframePad:3,
32492     hideMode:'offsets',
32493     
32494     tbContainer : false,
32495     
32496     bodyCls : '',
32497     
32498     toolbarContainer :function() {
32499         return this.wrap.select('.x-html-editor-tb',true).first();
32500     },
32501
32502     /**
32503      * Protected method that will not generally be called directly. It
32504      * is called when the editor creates its toolbar. Override this method if you need to
32505      * add custom toolbar buttons.
32506      * @param {HtmlEditor} editor
32507      */
32508     createToolbar : function(){
32509         Roo.log('renewing');
32510         Roo.log("create toolbars");
32511         
32512         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32513         this.toolbars[0].render(this.toolbarContainer());
32514         
32515         return;
32516         
32517 //        if (!editor.toolbars || !editor.toolbars.length) {
32518 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32519 //        }
32520 //        
32521 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32522 //            editor.toolbars[i] = Roo.factory(
32523 //                    typeof(editor.toolbars[i]) == 'string' ?
32524 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32525 //                Roo.bootstrap.form.HtmlEditor);
32526 //            editor.toolbars[i].init(editor);
32527 //        }
32528     },
32529
32530      
32531     // private
32532     onRender : function(ct, position)
32533     {
32534        // Roo.log("Call onRender: " + this.xtype);
32535         var _t = this;
32536         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32537       
32538         this.wrap = this.inputEl().wrap({
32539             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32540         });
32541         
32542         this.editorcore.onRender(ct, position);
32543          
32544          
32545         this.createToolbar(this);
32546        
32547         
32548           
32549         
32550     },
32551
32552     // private
32553     onResize : function(w, h)
32554     {
32555         Roo.log('resize: ' +w + ',' + h );
32556         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32557         var ew = false;
32558         var eh = false;
32559         
32560         if(this.inputEl() ){
32561             if(typeof w == 'number'){
32562                 var aw = w - this.wrap.getFrameWidth('lr');
32563                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32564                 ew = aw;
32565             }
32566             if(typeof h == 'number'){
32567                  var tbh = -11;  // fixme it needs to tool bar size!
32568                 for (var i =0; i < this.toolbars.length;i++) {
32569                     // fixme - ask toolbars for heights?
32570                     tbh += this.toolbars[i].el.getHeight();
32571                     //if (this.toolbars[i].footer) {
32572                     //    tbh += this.toolbars[i].footer.el.getHeight();
32573                     //}
32574                 }
32575               
32576                 
32577                 
32578                 
32579                 
32580                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32581                 ah -= 5; // knock a few pixes off for look..
32582                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32583                 var eh = ah;
32584             }
32585         }
32586         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32587         this.editorcore.onResize(ew,eh);
32588         
32589     },
32590
32591     /**
32592      * Toggles the editor between standard and source edit mode.
32593      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32594      */
32595     toggleSourceEdit : function(sourceEditMode)
32596     {
32597         this.editorcore.toggleSourceEdit(sourceEditMode);
32598         
32599         if(this.editorcore.sourceEditMode){
32600             Roo.log('editor - showing textarea');
32601             
32602 //            Roo.log('in');
32603 //            Roo.log(this.syncValue());
32604             this.syncValue();
32605             this.inputEl().removeClass(['hide', 'x-hidden']);
32606             this.inputEl().dom.removeAttribute('tabIndex');
32607             this.inputEl().focus();
32608         }else{
32609             Roo.log('editor - hiding textarea');
32610 //            Roo.log('out')
32611 //            Roo.log(this.pushValue()); 
32612             this.pushValue();
32613             
32614             this.inputEl().addClass(['hide', 'x-hidden']);
32615             this.inputEl().dom.setAttribute('tabIndex', -1);
32616             //this.deferFocus();
32617         }
32618          
32619         //if(this.resizable){
32620         //    this.setSize(this.wrap.getSize());
32621         //}
32622         
32623         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32624     },
32625  
32626     // private (for BoxComponent)
32627     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32628
32629     // private (for BoxComponent)
32630     getResizeEl : function(){
32631         return this.wrap;
32632     },
32633
32634     // private (for BoxComponent)
32635     getPositionEl : function(){
32636         return this.wrap;
32637     },
32638
32639     // private
32640     initEvents : function(){
32641         this.originalValue = this.getValue();
32642     },
32643
32644 //    /**
32645 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32646 //     * @method
32647 //     */
32648 //    markInvalid : Roo.emptyFn,
32649 //    /**
32650 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32651 //     * @method
32652 //     */
32653 //    clearInvalid : Roo.emptyFn,
32654
32655     setValue : function(v){
32656         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32657         this.editorcore.pushValue();
32658     },
32659
32660      
32661     // private
32662     deferFocus : function(){
32663         this.focus.defer(10, this);
32664     },
32665
32666     // doc'ed in Field
32667     focus : function(){
32668         this.editorcore.focus();
32669         
32670     },
32671       
32672
32673     // private
32674     onDestroy : function(){
32675         
32676         
32677         
32678         if(this.rendered){
32679             
32680             for (var i =0; i < this.toolbars.length;i++) {
32681                 // fixme - ask toolbars for heights?
32682                 this.toolbars[i].onDestroy();
32683             }
32684             
32685             this.wrap.dom.innerHTML = '';
32686             this.wrap.remove();
32687         }
32688     },
32689
32690     // private
32691     onFirstFocus : function(){
32692         //Roo.log("onFirstFocus");
32693         this.editorcore.onFirstFocus();
32694          for (var i =0; i < this.toolbars.length;i++) {
32695             this.toolbars[i].onFirstFocus();
32696         }
32697         
32698     },
32699     
32700     // private
32701     syncValue : function()
32702     {   
32703         this.editorcore.syncValue();
32704     },
32705     
32706     pushValue : function()
32707     {   
32708         this.editorcore.pushValue();
32709     }
32710      
32711     
32712     // hide stuff that is not compatible
32713     /**
32714      * @event blur
32715      * @hide
32716      */
32717     /**
32718      * @event change
32719      * @hide
32720      */
32721     /**
32722      * @event focus
32723      * @hide
32724      */
32725     /**
32726      * @event specialkey
32727      * @hide
32728      */
32729     /**
32730      * @cfg {String} fieldClass @hide
32731      */
32732     /**
32733      * @cfg {String} focusClass @hide
32734      */
32735     /**
32736      * @cfg {String} autoCreate @hide
32737      */
32738     /**
32739      * @cfg {String} inputType @hide
32740      */
32741      
32742     /**
32743      * @cfg {String} invalidText @hide
32744      */
32745     /**
32746      * @cfg {String} msgFx @hide
32747      */
32748     /**
32749      * @cfg {String} validateOnBlur @hide
32750      */
32751 });
32752  
32753     
32754    
32755    
32756    
32757       
32758 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32759 /**
32760  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32761  * @parent Roo.bootstrap.form.HtmlEditor
32762  * @extends Roo.bootstrap.nav.Simplebar
32763  * Basic Toolbar
32764  * 
32765  * @example
32766  * Usage:
32767  *
32768  new Roo.bootstrap.form.HtmlEditor({
32769     ....
32770     toolbars : [
32771         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32772             disable : { fonts: 1 , format: 1, ..., ... , ...],
32773             btns : [ .... ]
32774         })
32775     }
32776      
32777  * 
32778  * @cfg {Object} disable List of elements to disable..
32779  * @cfg {Array} btns List of additional buttons.
32780  * 
32781  * 
32782  * NEEDS Extra CSS? 
32783  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32784  */
32785  
32786 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32787 {
32788     
32789     Roo.apply(this, config);
32790     
32791     // default disabled, based on 'good practice'..
32792     this.disable = this.disable || {};
32793     Roo.applyIf(this.disable, {
32794         fontSize : true,
32795         colors : true,
32796         specialElements : true
32797     });
32798     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32799     
32800     this.editor = config.editor;
32801     this.editorcore = config.editor.editorcore;
32802     
32803     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32804     
32805     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32806     // dont call parent... till later.
32807 }
32808 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32809      
32810     bar : true,
32811     
32812     editor : false,
32813     editorcore : false,
32814     
32815     
32816     formats : [
32817         "p" ,  
32818         "h1","h2","h3","h4","h5","h6", 
32819         "pre", "code", 
32820         "abbr", "acronym", "address", "cite", "samp", "var",
32821         'div','span'
32822     ],
32823     
32824     onRender : function(ct, position)
32825     {
32826        // Roo.log("Call onRender: " + this.xtype);
32827         
32828        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32829        Roo.log(this.el);
32830        this.el.dom.style.marginBottom = '0';
32831        var _this = this;
32832        var editorcore = this.editorcore;
32833        var editor= this.editor;
32834        
32835        var children = [];
32836        var btn = function(id,cmd , toggle, handler, html){
32837        
32838             var  event = toggle ? 'toggle' : 'click';
32839        
32840             var a = {
32841                 size : 'sm',
32842                 xtype: 'Button',
32843                 xns: Roo.bootstrap,
32844                 //glyphicon : id,
32845                 fa: id,
32846                 cmd : id || cmd,
32847                 enableToggle:toggle !== false,
32848                 html : html || '',
32849                 pressed : toggle ? false : null,
32850                 listeners : {}
32851             };
32852             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32853                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32854             };
32855             children.push(a);
32856             return a;
32857        }
32858        
32859     //    var cb_box = function...
32860         
32861         var style = {
32862                 xtype: 'Button',
32863                 size : 'sm',
32864                 xns: Roo.bootstrap,
32865                 fa : 'font',
32866                 //html : 'submit'
32867                 menu : {
32868                     xtype: 'Menu',
32869                     xns: Roo.bootstrap,
32870                     items:  []
32871                 }
32872         };
32873         Roo.each(this.formats, function(f) {
32874             style.menu.items.push({
32875                 xtype :'MenuItem',
32876                 xns: Roo.bootstrap,
32877                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32878                 tagname : f,
32879                 listeners : {
32880                     click : function()
32881                     {
32882                         editorcore.insertTag(this.tagname);
32883                         editor.focus();
32884                     }
32885                 }
32886                 
32887             });
32888         });
32889         children.push(style);   
32890         
32891         btn('bold',false,true);
32892         btn('italic',false,true);
32893         btn('align-left', 'justifyleft',true);
32894         btn('align-center', 'justifycenter',true);
32895         btn('align-right' , 'justifyright',true);
32896         btn('link', false, false, function(btn) {
32897             //Roo.log("create link?");
32898             var url = prompt(this.createLinkText, this.defaultLinkValue);
32899             if(url && url != 'http:/'+'/'){
32900                 this.editorcore.relayCmd('createlink', url);
32901             }
32902         }),
32903         btn('list','insertunorderedlist',true);
32904         btn('pencil', false,true, function(btn){
32905                 Roo.log(this);
32906                 this.toggleSourceEdit(btn.pressed);
32907         });
32908         
32909         if (this.editor.btns.length > 0) {
32910             for (var i = 0; i<this.editor.btns.length; i++) {
32911                 children.push(this.editor.btns[i]);
32912             }
32913         }
32914         
32915         /*
32916         var cog = {
32917                 xtype: 'Button',
32918                 size : 'sm',
32919                 xns: Roo.bootstrap,
32920                 glyphicon : 'cog',
32921                 //html : 'submit'
32922                 menu : {
32923                     xtype: 'Menu',
32924                     xns: Roo.bootstrap,
32925                     items:  []
32926                 }
32927         };
32928         
32929         cog.menu.items.push({
32930             xtype :'MenuItem',
32931             xns: Roo.bootstrap,
32932             html : Clean styles,
32933             tagname : f,
32934             listeners : {
32935                 click : function()
32936                 {
32937                     editorcore.insertTag(this.tagname);
32938                     editor.focus();
32939                 }
32940             }
32941             
32942         });
32943        */
32944         
32945          
32946        this.xtype = 'NavSimplebar';
32947         
32948         for(var i=0;i< children.length;i++) {
32949             
32950             this.buttons.add(this.addxtypeChild(children[i]));
32951             
32952         }
32953         
32954         editor.on('editorevent', this.updateToolbar, this);
32955     },
32956     onBtnClick : function(id)
32957     {
32958        this.editorcore.relayCmd(id);
32959        this.editorcore.focus();
32960     },
32961     
32962     /**
32963      * Protected method that will not generally be called directly. It triggers
32964      * a toolbar update by reading the markup state of the current selection in the editor.
32965      */
32966     updateToolbar: function(){
32967
32968         if(!this.editorcore.activated){
32969             this.editor.onFirstFocus(); // is this neeed?
32970             return;
32971         }
32972
32973         var btns = this.buttons; 
32974         var doc = this.editorcore.doc;
32975         btns.get('bold').setActive(doc.queryCommandState('bold'));
32976         btns.get('italic').setActive(doc.queryCommandState('italic'));
32977         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32978         
32979         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32980         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32981         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32982         
32983         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32984         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32985          /*
32986         
32987         var ans = this.editorcore.getAllAncestors();
32988         if (this.formatCombo) {
32989             
32990             
32991             var store = this.formatCombo.store;
32992             this.formatCombo.setValue("");
32993             for (var i =0; i < ans.length;i++) {
32994                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32995                     // select it..
32996                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32997                     break;
32998                 }
32999             }
33000         }
33001         
33002         
33003         
33004         // hides menus... - so this cant be on a menu...
33005         Roo.bootstrap.MenuMgr.hideAll();
33006         */
33007         Roo.bootstrap.menu.Manager.hideAll();
33008         //this.editorsyncValue();
33009     },
33010     onFirstFocus: function() {
33011         this.buttons.each(function(item){
33012            item.enable();
33013         });
33014     },
33015     toggleSourceEdit : function(sourceEditMode){
33016         
33017           
33018         if(sourceEditMode){
33019             Roo.log("disabling buttons");
33020            this.buttons.each( function(item){
33021                 if(item.cmd != 'pencil'){
33022                     item.disable();
33023                 }
33024             });
33025           
33026         }else{
33027             Roo.log("enabling buttons");
33028             if(this.editorcore.initialized){
33029                 this.buttons.each( function(item){
33030                     item.enable();
33031                 });
33032             }
33033             
33034         }
33035         Roo.log("calling toggole on editor");
33036         // tell the editor that it's been pressed..
33037         this.editor.toggleSourceEdit(sourceEditMode);
33038        
33039     }
33040 });
33041
33042
33043
33044
33045  
33046 /*
33047  * - LGPL
33048  */
33049
33050 /**
33051  * @class Roo.bootstrap.form.Markdown
33052  * @extends Roo.bootstrap.form.TextArea
33053  * Bootstrap Showdown editable area
33054  * @cfg {string} content
33055  * 
33056  * @constructor
33057  * Create a new Showdown
33058  */
33059
33060 Roo.bootstrap.form.Markdown = function(config){
33061     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33062    
33063 };
33064
33065 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33066     
33067     editing :false,
33068     
33069     initEvents : function()
33070     {
33071         
33072         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33073         this.markdownEl = this.el.createChild({
33074             cls : 'roo-markdown-area'
33075         });
33076         this.inputEl().addClass('d-none');
33077         if (this.getValue() == '') {
33078             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33079             
33080         } else {
33081             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33082         }
33083         this.markdownEl.on('click', this.toggleTextEdit, this);
33084         this.on('blur', this.toggleTextEdit, this);
33085         this.on('specialkey', this.resizeTextArea, this);
33086     },
33087     
33088     toggleTextEdit : function()
33089     {
33090         var sh = this.markdownEl.getHeight();
33091         this.inputEl().addClass('d-none');
33092         this.markdownEl.addClass('d-none');
33093         if (!this.editing) {
33094             // show editor?
33095             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33096             this.inputEl().removeClass('d-none');
33097             this.inputEl().focus();
33098             this.editing = true;
33099             return;
33100         }
33101         // show showdown...
33102         this.updateMarkdown();
33103         this.markdownEl.removeClass('d-none');
33104         this.editing = false;
33105         return;
33106     },
33107     updateMarkdown : function()
33108     {
33109         if (this.getValue() == '') {
33110             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33111             return;
33112         }
33113  
33114         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33115     },
33116     
33117     resizeTextArea: function () {
33118         
33119         var sh = 100;
33120         Roo.log([sh, this.getValue().split("\n").length * 30]);
33121         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33122     },
33123     setValue : function(val)
33124     {
33125         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33126         if (!this.editing) {
33127             this.updateMarkdown();
33128         }
33129         
33130     },
33131     focus : function()
33132     {
33133         if (!this.editing) {
33134             this.toggleTextEdit();
33135         }
33136         
33137     }
33138
33139
33140 });/*
33141  * Based on:
33142  * Ext JS Library 1.1.1
33143  * Copyright(c) 2006-2007, Ext JS, LLC.
33144  *
33145  * Originally Released Under LGPL - original licence link has changed is not relivant.
33146  *
33147  * Fork - LGPL
33148  * <script type="text/javascript">
33149  */
33150  
33151 /**
33152  * @class Roo.bootstrap.PagingToolbar
33153  * @extends Roo.bootstrap.nav.Simplebar
33154  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33155  * @constructor
33156  * Create a new PagingToolbar
33157  * @param {Object} config The config object
33158  * @param {Roo.data.Store} store
33159  */
33160 Roo.bootstrap.PagingToolbar = function(config)
33161 {
33162     // old args format still supported... - xtype is prefered..
33163         // created from xtype...
33164     
33165     this.ds = config.dataSource;
33166     
33167     if (config.store && !this.ds) {
33168         this.store= Roo.factory(config.store, Roo.data);
33169         this.ds = this.store;
33170         this.ds.xmodule = this.xmodule || false;
33171     }
33172     
33173     this.toolbarItems = [];
33174     if (config.items) {
33175         this.toolbarItems = config.items;
33176     }
33177     
33178     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33179     
33180     this.cursor = 0;
33181     
33182     if (this.ds) { 
33183         this.bind(this.ds);
33184     }
33185     
33186     if (Roo.bootstrap.version == 4) {
33187         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33188     } else {
33189         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33190     }
33191     
33192 };
33193
33194 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33195     /**
33196      * @cfg {Roo.bootstrap.Button} buttons[]
33197      * Buttons for the toolbar
33198      */
33199      /**
33200      * @cfg {Roo.data.Store} store
33201      * The underlying data store providing the paged data
33202      */
33203     /**
33204      * @cfg {String/HTMLElement/Element} container
33205      * container The id or element that will contain the toolbar
33206      */
33207     /**
33208      * @cfg {Boolean} displayInfo
33209      * True to display the displayMsg (defaults to false)
33210      */
33211     /**
33212      * @cfg {Number} pageSize
33213      * The number of records to display per page (defaults to 20)
33214      */
33215     pageSize: 20,
33216     /**
33217      * @cfg {String} displayMsg
33218      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33219      */
33220     displayMsg : 'Displaying {0} - {1} of {2}',
33221     /**
33222      * @cfg {String} emptyMsg
33223      * The message to display when no records are found (defaults to "No data to display")
33224      */
33225     emptyMsg : 'No data to display',
33226     /**
33227      * Customizable piece of the default paging text (defaults to "Page")
33228      * @type String
33229      */
33230     beforePageText : "Page",
33231     /**
33232      * Customizable piece of the default paging text (defaults to "of %0")
33233      * @type String
33234      */
33235     afterPageText : "of {0}",
33236     /**
33237      * Customizable piece of the default paging text (defaults to "First Page")
33238      * @type String
33239      */
33240     firstText : "First Page",
33241     /**
33242      * Customizable piece of the default paging text (defaults to "Previous Page")
33243      * @type String
33244      */
33245     prevText : "Previous Page",
33246     /**
33247      * Customizable piece of the default paging text (defaults to "Next Page")
33248      * @type String
33249      */
33250     nextText : "Next Page",
33251     /**
33252      * Customizable piece of the default paging text (defaults to "Last Page")
33253      * @type String
33254      */
33255     lastText : "Last Page",
33256     /**
33257      * Customizable piece of the default paging text (defaults to "Refresh")
33258      * @type String
33259      */
33260     refreshText : "Refresh",
33261
33262     buttons : false,
33263     // private
33264     onRender : function(ct, position) 
33265     {
33266         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33267         this.navgroup.parentId = this.id;
33268         this.navgroup.onRender(this.el, null);
33269         // add the buttons to the navgroup
33270         
33271         if(this.displayInfo){
33272             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33273             this.displayEl = this.el.select('.x-paging-info', true).first();
33274 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33275 //            this.displayEl = navel.el.select('span',true).first();
33276         }
33277         
33278         var _this = this;
33279         
33280         if(this.buttons){
33281             Roo.each(_this.buttons, function(e){ // this might need to use render????
33282                Roo.factory(e).render(_this.el);
33283             });
33284         }
33285             
33286         Roo.each(_this.toolbarItems, function(e) {
33287             _this.navgroup.addItem(e);
33288         });
33289         
33290         
33291         this.first = this.navgroup.addItem({
33292             tooltip: this.firstText,
33293             cls: "prev btn-outline-secondary",
33294             html : ' <i class="fa fa-step-backward"></i>',
33295             disabled: true,
33296             preventDefault: true,
33297             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33298         });
33299         
33300         this.prev =  this.navgroup.addItem({
33301             tooltip: this.prevText,
33302             cls: "prev btn-outline-secondary",
33303             html : ' <i class="fa fa-backward"></i>',
33304             disabled: true,
33305             preventDefault: true,
33306             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33307         });
33308     //this.addSeparator();
33309         
33310         
33311         var field = this.navgroup.addItem( {
33312             tagtype : 'span',
33313             cls : 'x-paging-position  btn-outline-secondary',
33314              disabled: true,
33315             html : this.beforePageText  +
33316                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33317                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33318          } ); //?? escaped?
33319         
33320         this.field = field.el.select('input', true).first();
33321         this.field.on("keydown", this.onPagingKeydown, this);
33322         this.field.on("focus", function(){this.dom.select();});
33323     
33324     
33325         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33326         //this.field.setHeight(18);
33327         //this.addSeparator();
33328         this.next = this.navgroup.addItem({
33329             tooltip: this.nextText,
33330             cls: "next btn-outline-secondary",
33331             html : ' <i class="fa fa-forward"></i>',
33332             disabled: true,
33333             preventDefault: true,
33334             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33335         });
33336         this.last = this.navgroup.addItem({
33337             tooltip: this.lastText,
33338             html : ' <i class="fa fa-step-forward"></i>',
33339             cls: "next btn-outline-secondary",
33340             disabled: true,
33341             preventDefault: true,
33342             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33343         });
33344     //this.addSeparator();
33345         this.loading = this.navgroup.addItem({
33346             tooltip: this.refreshText,
33347             cls: "btn-outline-secondary",
33348             html : ' <i class="fa fa-refresh"></i>',
33349             preventDefault: true,
33350             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33351         });
33352         
33353     },
33354
33355     // private
33356     updateInfo : function(){
33357         if(this.displayEl){
33358             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33359             var msg = count == 0 ?
33360                 this.emptyMsg :
33361                 String.format(
33362                     this.displayMsg,
33363                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33364                 );
33365             this.displayEl.update(msg);
33366         }
33367     },
33368
33369     // private
33370     onLoad : function(ds, r, o)
33371     {
33372         this.cursor = o.params && o.params.start ? o.params.start : 0;
33373         
33374         var d = this.getPageData(),
33375             ap = d.activePage,
33376             ps = d.pages;
33377         
33378         
33379         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33380         this.field.dom.value = ap;
33381         this.first.setDisabled(ap == 1);
33382         this.prev.setDisabled(ap == 1);
33383         this.next.setDisabled(ap == ps);
33384         this.last.setDisabled(ap == ps);
33385         this.loading.enable();
33386         this.updateInfo();
33387     },
33388
33389     // private
33390     getPageData : function(){
33391         var total = this.ds.getTotalCount();
33392         return {
33393             total : total,
33394             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33395             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33396         };
33397     },
33398
33399     // private
33400     onLoadError : function(proxy, o){
33401         this.loading.enable();
33402         if (this.ds.events.loadexception.listeners.length  < 2) {
33403             // nothing has been assigned to loadexception except this...
33404             // so 
33405             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33406
33407         }
33408     },
33409
33410     // private
33411     onPagingKeydown : function(e){
33412         var k = e.getKey();
33413         var d = this.getPageData();
33414         if(k == e.RETURN){
33415             var v = this.field.dom.value, pageNum;
33416             if(!v || isNaN(pageNum = parseInt(v, 10))){
33417                 this.field.dom.value = d.activePage;
33418                 return;
33419             }
33420             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33421             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33422             e.stopEvent();
33423         }
33424         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))
33425         {
33426           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33427           this.field.dom.value = pageNum;
33428           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33429           e.stopEvent();
33430         }
33431         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33432         {
33433           var v = this.field.dom.value, pageNum; 
33434           var increment = (e.shiftKey) ? 10 : 1;
33435           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33436                 increment *= -1;
33437           }
33438           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33439             this.field.dom.value = d.activePage;
33440             return;
33441           }
33442           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33443           {
33444             this.field.dom.value = parseInt(v, 10) + increment;
33445             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33446             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33447           }
33448           e.stopEvent();
33449         }
33450     },
33451
33452     // private
33453     beforeLoad : function(){
33454         if(this.loading){
33455             this.loading.disable();
33456         }
33457     },
33458
33459     // private
33460     onClick : function(which){
33461         
33462         var ds = this.ds;
33463         if (!ds) {
33464             return;
33465         }
33466         
33467         switch(which){
33468             case "first":
33469                 ds.load({params:{start: 0, limit: this.pageSize}});
33470             break;
33471             case "prev":
33472                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33473             break;
33474             case "next":
33475                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33476             break;
33477             case "last":
33478                 var total = ds.getTotalCount();
33479                 var extra = total % this.pageSize;
33480                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33481                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33482             break;
33483             case "refresh":
33484                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33485             break;
33486         }
33487     },
33488
33489     /**
33490      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33491      * @param {Roo.data.Store} store The data store to unbind
33492      */
33493     unbind : function(ds){
33494         ds.un("beforeload", this.beforeLoad, this);
33495         ds.un("load", this.onLoad, this);
33496         ds.un("loadexception", this.onLoadError, this);
33497         ds.un("remove", this.updateInfo, this);
33498         ds.un("add", this.updateInfo, this);
33499         this.ds = undefined;
33500     },
33501
33502     /**
33503      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33504      * @param {Roo.data.Store} store The data store to bind
33505      */
33506     bind : function(ds){
33507         ds.on("beforeload", this.beforeLoad, this);
33508         ds.on("load", this.onLoad, this);
33509         ds.on("loadexception", this.onLoadError, this);
33510         ds.on("remove", this.updateInfo, this);
33511         ds.on("add", this.updateInfo, this);
33512         this.ds = ds;
33513     }
33514 });/*
33515  * - LGPL
33516  *
33517  * element
33518  * 
33519  */
33520
33521 /**
33522  * @class Roo.bootstrap.MessageBar
33523  * @extends Roo.bootstrap.Component
33524  * Bootstrap MessageBar class
33525  * @cfg {String} html contents of the MessageBar
33526  * @cfg {String} weight (info | success | warning | danger) default info
33527  * @cfg {String} beforeClass insert the bar before the given class
33528  * @cfg {Boolean} closable (true | false) default false
33529  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33530  * 
33531  * @constructor
33532  * Create a new Element
33533  * @param {Object} config The config object
33534  */
33535
33536 Roo.bootstrap.MessageBar = function(config){
33537     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33538 };
33539
33540 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33541     
33542     html: '',
33543     weight: 'info',
33544     closable: false,
33545     fixed: false,
33546     beforeClass: 'bootstrap-sticky-wrap',
33547     
33548     getAutoCreate : function(){
33549         
33550         var cfg = {
33551             tag: 'div',
33552             cls: 'alert alert-dismissable alert-' + this.weight,
33553             cn: [
33554                 {
33555                     tag: 'span',
33556                     cls: 'message',
33557                     html: this.html || ''
33558                 }
33559             ]
33560         };
33561         
33562         if(this.fixed){
33563             cfg.cls += ' alert-messages-fixed';
33564         }
33565         
33566         if(this.closable){
33567             cfg.cn.push({
33568                 tag: 'button',
33569                 cls: 'close',
33570                 html: 'x'
33571             });
33572         }
33573         
33574         return cfg;
33575     },
33576     
33577     onRender : function(ct, position)
33578     {
33579         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33580         
33581         if(!this.el){
33582             var cfg = Roo.apply({},  this.getAutoCreate());
33583             cfg.id = Roo.id();
33584             
33585             if (this.cls) {
33586                 cfg.cls += ' ' + this.cls;
33587             }
33588             if (this.style) {
33589                 cfg.style = this.style;
33590             }
33591             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33592             
33593             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33594         }
33595         
33596         this.el.select('>button.close').on('click', this.hide, this);
33597         
33598     },
33599     
33600     show : function()
33601     {
33602         if (!this.rendered) {
33603             this.render();
33604         }
33605         
33606         this.el.show();
33607         
33608         this.fireEvent('show', this);
33609         
33610     },
33611     
33612     hide : function()
33613     {
33614         if (!this.rendered) {
33615             this.render();
33616         }
33617         
33618         this.el.hide();
33619         
33620         this.fireEvent('hide', this);
33621     },
33622     
33623     update : function()
33624     {
33625 //        var e = this.el.dom.firstChild;
33626 //        
33627 //        if(this.closable){
33628 //            e = e.nextSibling;
33629 //        }
33630 //        
33631 //        e.data = this.html || '';
33632
33633         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33634     }
33635    
33636 });
33637
33638  
33639
33640      /*
33641  * - LGPL
33642  *
33643  * Graph
33644  * 
33645  */
33646
33647
33648 /**
33649  * @class Roo.bootstrap.Graph
33650  * @extends Roo.bootstrap.Component
33651  * Bootstrap Graph class
33652 > Prameters
33653  -sm {number} sm 4
33654  -md {number} md 5
33655  @cfg {String} graphtype  bar | vbar | pie
33656  @cfg {number} g_x coodinator | centre x (pie)
33657  @cfg {number} g_y coodinator | centre y (pie)
33658  @cfg {number} g_r radius (pie)
33659  @cfg {number} g_height height of the chart (respected by all elements in the set)
33660  @cfg {number} g_width width of the chart (respected by all elements in the set)
33661  @cfg {Object} title The title of the chart
33662     
33663  -{Array}  values
33664  -opts (object) options for the chart 
33665      o {
33666      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33667      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33668      o vgutter (number)
33669      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.
33670      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33671      o to
33672      o stretch (boolean)
33673      o }
33674  -opts (object) options for the pie
33675      o{
33676      o cut
33677      o startAngle (number)
33678      o endAngle (number)
33679      } 
33680  *
33681  * @constructor
33682  * Create a new Input
33683  * @param {Object} config The config object
33684  */
33685
33686 Roo.bootstrap.Graph = function(config){
33687     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33688     
33689     this.addEvents({
33690         // img events
33691         /**
33692          * @event click
33693          * The img click event for the img.
33694          * @param {Roo.EventObject} e
33695          */
33696         "click" : true
33697     });
33698 };
33699
33700 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33701     
33702     sm: 4,
33703     md: 5,
33704     graphtype: 'bar',
33705     g_height: 250,
33706     g_width: 400,
33707     g_x: 50,
33708     g_y: 50,
33709     g_r: 30,
33710     opts:{
33711         //g_colors: this.colors,
33712         g_type: 'soft',
33713         g_gutter: '20%'
33714
33715     },
33716     title : false,
33717
33718     getAutoCreate : function(){
33719         
33720         var cfg = {
33721             tag: 'div',
33722             html : null
33723         };
33724         
33725         
33726         return  cfg;
33727     },
33728
33729     onRender : function(ct,position){
33730         
33731         
33732         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33733         
33734         if (typeof(Raphael) == 'undefined') {
33735             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33736             return;
33737         }
33738         
33739         this.raphael = Raphael(this.el.dom);
33740         
33741                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33742                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33743                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33744                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33745                 /*
33746                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33747                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33748                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33749                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33750                 
33751                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33752                 r.barchart(330, 10, 300, 220, data1);
33753                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33754                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33755                 */
33756                 
33757                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33758                 // r.barchart(30, 30, 560, 250,  xdata, {
33759                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33760                 //     axis : "0 0 1 1",
33761                 //     axisxlabels :  xdata
33762                 //     //yvalues : cols,
33763                    
33764                 // });
33765 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33766 //        
33767 //        this.load(null,xdata,{
33768 //                axis : "0 0 1 1",
33769 //                axisxlabels :  xdata
33770 //                });
33771
33772     },
33773
33774     load : function(graphtype,xdata,opts)
33775     {
33776         this.raphael.clear();
33777         if(!graphtype) {
33778             graphtype = this.graphtype;
33779         }
33780         if(!opts){
33781             opts = this.opts;
33782         }
33783         var r = this.raphael,
33784             fin = function () {
33785                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33786             },
33787             fout = function () {
33788                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33789             },
33790             pfin = function() {
33791                 this.sector.stop();
33792                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33793
33794                 if (this.label) {
33795                     this.label[0].stop();
33796                     this.label[0].attr({ r: 7.5 });
33797                     this.label[1].attr({ "font-weight": 800 });
33798                 }
33799             },
33800             pfout = function() {
33801                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33802
33803                 if (this.label) {
33804                     this.label[0].animate({ r: 5 }, 500, "bounce");
33805                     this.label[1].attr({ "font-weight": 400 });
33806                 }
33807             };
33808
33809         switch(graphtype){
33810             case 'bar':
33811                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33812                 break;
33813             case 'hbar':
33814                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33815                 break;
33816             case 'pie':
33817 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33818 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33819 //            
33820                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33821                 
33822                 break;
33823
33824         }
33825         
33826         if(this.title){
33827             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33828         }
33829         
33830     },
33831     
33832     setTitle: function(o)
33833     {
33834         this.title = o;
33835     },
33836     
33837     initEvents: function() {
33838         
33839         if(!this.href){
33840             this.el.on('click', this.onClick, this);
33841         }
33842     },
33843     
33844     onClick : function(e)
33845     {
33846         Roo.log('img onclick');
33847         this.fireEvent('click', this, e);
33848     }
33849    
33850 });
33851
33852  
33853 Roo.bootstrap.dash = {};/*
33854  * - LGPL
33855  *
33856  * numberBox
33857  * 
33858  */
33859 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33860
33861 /**
33862  * @class Roo.bootstrap.dash.NumberBox
33863  * @extends Roo.bootstrap.Component
33864  * Bootstrap NumberBox class
33865  * @cfg {String} headline Box headline
33866  * @cfg {String} content Box content
33867  * @cfg {String} icon Box icon
33868  * @cfg {String} footer Footer text
33869  * @cfg {String} fhref Footer href
33870  * 
33871  * @constructor
33872  * Create a new NumberBox
33873  * @param {Object} config The config object
33874  */
33875
33876
33877 Roo.bootstrap.dash.NumberBox = function(config){
33878     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33879     
33880 };
33881
33882 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33883     
33884     headline : '',
33885     content : '',
33886     icon : '',
33887     footer : '',
33888     fhref : '',
33889     ficon : '',
33890     
33891     getAutoCreate : function(){
33892         
33893         var cfg = {
33894             tag : 'div',
33895             cls : 'small-box ',
33896             cn : [
33897                 {
33898                     tag : 'div',
33899                     cls : 'inner',
33900                     cn :[
33901                         {
33902                             tag : 'h3',
33903                             cls : 'roo-headline',
33904                             html : this.headline
33905                         },
33906                         {
33907                             tag : 'p',
33908                             cls : 'roo-content',
33909                             html : this.content
33910                         }
33911                     ]
33912                 }
33913             ]
33914         };
33915         
33916         if(this.icon){
33917             cfg.cn.push({
33918                 tag : 'div',
33919                 cls : 'icon',
33920                 cn :[
33921                     {
33922                         tag : 'i',
33923                         cls : 'ion ' + this.icon
33924                     }
33925                 ]
33926             });
33927         }
33928         
33929         if(this.footer){
33930             var footer = {
33931                 tag : 'a',
33932                 cls : 'small-box-footer',
33933                 href : this.fhref || '#',
33934                 html : this.footer
33935             };
33936             
33937             cfg.cn.push(footer);
33938             
33939         }
33940         
33941         return  cfg;
33942     },
33943
33944     onRender : function(ct,position){
33945         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33946
33947
33948        
33949                 
33950     },
33951
33952     setHeadline: function (value)
33953     {
33954         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33955     },
33956     
33957     setFooter: function (value, href)
33958     {
33959         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33960         
33961         if(href){
33962             this.el.select('a.small-box-footer',true).first().attr('href', href);
33963         }
33964         
33965     },
33966
33967     setContent: function (value)
33968     {
33969         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33970     },
33971
33972     initEvents: function() 
33973     {   
33974         
33975     }
33976     
33977 });
33978
33979  
33980 /*
33981  * - LGPL
33982  *
33983  * TabBox
33984  * 
33985  */
33986 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33987
33988 /**
33989  * @class Roo.bootstrap.dash.TabBox
33990  * @extends Roo.bootstrap.Component
33991  * @children Roo.bootstrap.dash.TabPane
33992  * Bootstrap TabBox class
33993  * @cfg {String} title Title of the TabBox
33994  * @cfg {String} icon Icon of the TabBox
33995  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33996  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33997  * 
33998  * @constructor
33999  * Create a new TabBox
34000  * @param {Object} config The config object
34001  */
34002
34003
34004 Roo.bootstrap.dash.TabBox = function(config){
34005     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34006     this.addEvents({
34007         // raw events
34008         /**
34009          * @event addpane
34010          * When a pane is added
34011          * @param {Roo.bootstrap.dash.TabPane} pane
34012          */
34013         "addpane" : true,
34014         /**
34015          * @event activatepane
34016          * When a pane is activated
34017          * @param {Roo.bootstrap.dash.TabPane} pane
34018          */
34019         "activatepane" : true
34020         
34021          
34022     });
34023     
34024     this.panes = [];
34025 };
34026
34027 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34028
34029     title : '',
34030     icon : false,
34031     showtabs : true,
34032     tabScrollable : false,
34033     
34034     getChildContainer : function()
34035     {
34036         return this.el.select('.tab-content', true).first();
34037     },
34038     
34039     getAutoCreate : function(){
34040         
34041         var header = {
34042             tag: 'li',
34043             cls: 'pull-left header',
34044             html: this.title,
34045             cn : []
34046         };
34047         
34048         if(this.icon){
34049             header.cn.push({
34050                 tag: 'i',
34051                 cls: 'fa ' + this.icon
34052             });
34053         }
34054         
34055         var h = {
34056             tag: 'ul',
34057             cls: 'nav nav-tabs pull-right',
34058             cn: [
34059                 header
34060             ]
34061         };
34062         
34063         if(this.tabScrollable){
34064             h = {
34065                 tag: 'div',
34066                 cls: 'tab-header',
34067                 cn: [
34068                     {
34069                         tag: 'ul',
34070                         cls: 'nav nav-tabs pull-right',
34071                         cn: [
34072                             header
34073                         ]
34074                     }
34075                 ]
34076             };
34077         }
34078         
34079         var cfg = {
34080             tag: 'div',
34081             cls: 'nav-tabs-custom',
34082             cn: [
34083                 h,
34084                 {
34085                     tag: 'div',
34086                     cls: 'tab-content no-padding',
34087                     cn: []
34088                 }
34089             ]
34090         };
34091
34092         return  cfg;
34093     },
34094     initEvents : function()
34095     {
34096         //Roo.log('add add pane handler');
34097         this.on('addpane', this.onAddPane, this);
34098     },
34099      /**
34100      * Updates the box title
34101      * @param {String} html to set the title to.
34102      */
34103     setTitle : function(value)
34104     {
34105         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34106     },
34107     onAddPane : function(pane)
34108     {
34109         this.panes.push(pane);
34110         //Roo.log('addpane');
34111         //Roo.log(pane);
34112         // tabs are rendere left to right..
34113         if(!this.showtabs){
34114             return;
34115         }
34116         
34117         var ctr = this.el.select('.nav-tabs', true).first();
34118          
34119          
34120         var existing = ctr.select('.nav-tab',true);
34121         var qty = existing.getCount();;
34122         
34123         
34124         var tab = ctr.createChild({
34125             tag : 'li',
34126             cls : 'nav-tab' + (qty ? '' : ' active'),
34127             cn : [
34128                 {
34129                     tag : 'a',
34130                     href:'#',
34131                     html : pane.title
34132                 }
34133             ]
34134         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34135         pane.tab = tab;
34136         
34137         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34138         if (!qty) {
34139             pane.el.addClass('active');
34140         }
34141         
34142                 
34143     },
34144     onTabClick : function(ev,un,ob,pane)
34145     {
34146         //Roo.log('tab - prev default');
34147         ev.preventDefault();
34148         
34149         
34150         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34151         pane.tab.addClass('active');
34152         //Roo.log(pane.title);
34153         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34154         // technically we should have a deactivate event.. but maybe add later.
34155         // and it should not de-activate the selected tab...
34156         this.fireEvent('activatepane', pane);
34157         pane.el.addClass('active');
34158         pane.fireEvent('activate');
34159         
34160         
34161     },
34162     
34163     getActivePane : function()
34164     {
34165         var r = false;
34166         Roo.each(this.panes, function(p) {
34167             if(p.el.hasClass('active')){
34168                 r = p;
34169                 return false;
34170             }
34171             
34172             return;
34173         });
34174         
34175         return r;
34176     }
34177     
34178     
34179 });
34180
34181  
34182 /*
34183  * - LGPL
34184  *
34185  * Tab pane
34186  * 
34187  */
34188 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34189 /**
34190  * @class Roo.bootstrap.TabPane
34191  * @extends Roo.bootstrap.Component
34192  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34193  * Bootstrap TabPane class
34194  * @cfg {Boolean} active (false | true) Default false
34195  * @cfg {String} title title of panel
34196
34197  * 
34198  * @constructor
34199  * Create a new TabPane
34200  * @param {Object} config The config object
34201  */
34202
34203 Roo.bootstrap.dash.TabPane = function(config){
34204     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34205     
34206     this.addEvents({
34207         // raw events
34208         /**
34209          * @event activate
34210          * When a pane is activated
34211          * @param {Roo.bootstrap.dash.TabPane} pane
34212          */
34213         "activate" : true
34214          
34215     });
34216 };
34217
34218 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34219     
34220     active : false,
34221     title : '',
34222     
34223     // the tabBox that this is attached to.
34224     tab : false,
34225      
34226     getAutoCreate : function() 
34227     {
34228         var cfg = {
34229             tag: 'div',
34230             cls: 'tab-pane'
34231         };
34232         
34233         if(this.active){
34234             cfg.cls += ' active';
34235         }
34236         
34237         return cfg;
34238     },
34239     initEvents  : function()
34240     {
34241         //Roo.log('trigger add pane handler');
34242         this.parent().fireEvent('addpane', this)
34243     },
34244     
34245      /**
34246      * Updates the tab title 
34247      * @param {String} html to set the title to.
34248      */
34249     setTitle: function(str)
34250     {
34251         if (!this.tab) {
34252             return;
34253         }
34254         this.title = str;
34255         this.tab.select('a', true).first().dom.innerHTML = str;
34256         
34257     }
34258     
34259     
34260     
34261 });
34262
34263  
34264
34265
34266  /*
34267  * - LGPL
34268  *
34269  * Tooltip
34270  * 
34271  */
34272
34273 /**
34274  * @class Roo.bootstrap.Tooltip
34275  * Bootstrap Tooltip class
34276  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34277  * to determine which dom element triggers the tooltip.
34278  * 
34279  * It needs to add support for additional attributes like tooltip-position
34280  * 
34281  * @constructor
34282  * Create a new Toolti
34283  * @param {Object} config The config object
34284  */
34285
34286 Roo.bootstrap.Tooltip = function(config){
34287     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34288     
34289     this.alignment = Roo.bootstrap.Tooltip.alignment;
34290     
34291     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34292         this.alignment = config.alignment;
34293     }
34294     
34295 };
34296
34297 Roo.apply(Roo.bootstrap.Tooltip, {
34298     /**
34299      * @function init initialize tooltip monitoring.
34300      * @static
34301      */
34302     currentEl : false,
34303     currentTip : false,
34304     currentRegion : false,
34305     
34306     //  init : delay?
34307     
34308     init : function()
34309     {
34310         Roo.get(document).on('mouseover', this.enter ,this);
34311         Roo.get(document).on('mouseout', this.leave, this);
34312          
34313         
34314         this.currentTip = new Roo.bootstrap.Tooltip();
34315     },
34316     
34317     enter : function(ev)
34318     {
34319         var dom = ev.getTarget();
34320         
34321         //Roo.log(['enter',dom]);
34322         var el = Roo.fly(dom);
34323         if (this.currentEl) {
34324             //Roo.log(dom);
34325             //Roo.log(this.currentEl);
34326             //Roo.log(this.currentEl.contains(dom));
34327             if (this.currentEl == el) {
34328                 return;
34329             }
34330             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34331                 return;
34332             }
34333
34334         }
34335         
34336         if (this.currentTip.el) {
34337             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34338         }    
34339         //Roo.log(ev);
34340         
34341         if(!el || el.dom == document){
34342             return;
34343         }
34344         
34345         var bindEl = el; 
34346         var pel = false;
34347         if (!el.attr('tooltip')) {
34348             pel = el.findParent("[tooltip]");
34349             if (pel) {
34350                 bindEl = Roo.get(pel);
34351             }
34352         }
34353         
34354        
34355         
34356         // you can not look for children, as if el is the body.. then everythign is the child..
34357         if (!pel && !el.attr('tooltip')) { //
34358             if (!el.select("[tooltip]").elements.length) {
34359                 return;
34360             }
34361             // is the mouse over this child...?
34362             bindEl = el.select("[tooltip]").first();
34363             var xy = ev.getXY();
34364             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34365                 //Roo.log("not in region.");
34366                 return;
34367             }
34368             //Roo.log("child element over..");
34369             
34370         }
34371         this.currentEl = el;
34372         this.currentTip.bind(bindEl);
34373         this.currentRegion = Roo.lib.Region.getRegion(dom);
34374         this.currentTip.enter();
34375         
34376     },
34377     leave : function(ev)
34378     {
34379         var dom = ev.getTarget();
34380         //Roo.log(['leave',dom]);
34381         if (!this.currentEl) {
34382             return;
34383         }
34384         
34385         
34386         if (dom != this.currentEl.dom) {
34387             return;
34388         }
34389         var xy = ev.getXY();
34390         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34391             return;
34392         }
34393         // only activate leave if mouse cursor is outside... bounding box..
34394         
34395         
34396         
34397         
34398         if (this.currentTip) {
34399             this.currentTip.leave();
34400         }
34401         //Roo.log('clear currentEl');
34402         this.currentEl = false;
34403         
34404         
34405     },
34406     alignment : {
34407         'left' : ['r-l', [-2,0], 'right'],
34408         'right' : ['l-r', [2,0], 'left'],
34409         'bottom' : ['t-b', [0,2], 'top'],
34410         'top' : [ 'b-t', [0,-2], 'bottom']
34411     }
34412     
34413 });
34414
34415
34416 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34417     
34418     
34419     bindEl : false,
34420     
34421     delay : null, // can be { show : 300 , hide: 500}
34422     
34423     timeout : null,
34424     
34425     hoverState : null, //???
34426     
34427     placement : 'bottom', 
34428     
34429     alignment : false,
34430     
34431     getAutoCreate : function(){
34432     
34433         var cfg = {
34434            cls : 'tooltip',   
34435            role : 'tooltip',
34436            cn : [
34437                 {
34438                     cls : 'tooltip-arrow arrow'
34439                 },
34440                 {
34441                     cls : 'tooltip-inner'
34442                 }
34443            ]
34444         };
34445         
34446         return cfg;
34447     },
34448     bind : function(el)
34449     {
34450         this.bindEl = el;
34451     },
34452     
34453     initEvents : function()
34454     {
34455         this.arrowEl = this.el.select('.arrow', true).first();
34456         this.innerEl = this.el.select('.tooltip-inner', true).first();
34457     },
34458     
34459     enter : function () {
34460        
34461         if (this.timeout != null) {
34462             clearTimeout(this.timeout);
34463         }
34464         
34465         this.hoverState = 'in';
34466          //Roo.log("enter - show");
34467         if (!this.delay || !this.delay.show) {
34468             this.show();
34469             return;
34470         }
34471         var _t = this;
34472         this.timeout = setTimeout(function () {
34473             if (_t.hoverState == 'in') {
34474                 _t.show();
34475             }
34476         }, this.delay.show);
34477     },
34478     leave : function()
34479     {
34480         clearTimeout(this.timeout);
34481     
34482         this.hoverState = 'out';
34483          if (!this.delay || !this.delay.hide) {
34484             this.hide();
34485             return;
34486         }
34487        
34488         var _t = this;
34489         this.timeout = setTimeout(function () {
34490             //Roo.log("leave - timeout");
34491             
34492             if (_t.hoverState == 'out') {
34493                 _t.hide();
34494                 Roo.bootstrap.Tooltip.currentEl = false;
34495             }
34496         }, delay);
34497     },
34498     
34499     show : function (msg)
34500     {
34501         if (!this.el) {
34502             this.render(document.body);
34503         }
34504         // set content.
34505         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34506         
34507         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34508         
34509         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34510         
34511         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34512                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34513
34514         if(this.bindEl.attr('tooltip-class')) {
34515             this.el.addClass(this.bindEl.attr('tooltip-class'));
34516         }
34517         
34518         var placement = typeof this.placement == 'function' ?
34519             this.placement.call(this, this.el, on_el) :
34520             this.placement;
34521         
34522         if(this.bindEl.attr('tooltip-placement')) {
34523             placement = this.bindEl.attr('tooltip-placement');
34524         }
34525             
34526         var autoToken = /\s?auto?\s?/i;
34527         var autoPlace = autoToken.test(placement);
34528         if (autoPlace) {
34529             placement = placement.replace(autoToken, '') || 'top';
34530         }
34531         
34532         //this.el.detach()
34533         //this.el.setXY([0,0]);
34534         this.el.show();
34535         //this.el.dom.style.display='block';
34536         
34537         //this.el.appendTo(on_el);
34538         
34539         var p = this.getPosition();
34540         var box = this.el.getBox();
34541         
34542         if (autoPlace) {
34543             // fixme..
34544         }
34545         
34546         var align = this.alignment[placement];
34547         
34548         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34549         
34550         if(placement == 'top' || placement == 'bottom'){
34551             if(xy[0] < 0){
34552                 placement = 'right';
34553             }
34554             
34555             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34556                 placement = 'left';
34557             }
34558             
34559             var scroll = Roo.select('body', true).first().getScroll();
34560             
34561             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34562                 placement = 'top';
34563             }
34564             
34565             align = this.alignment[placement];
34566             
34567             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34568             
34569         }
34570         
34571         var elems = document.getElementsByTagName('div');
34572         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34573         for (var i = 0; i < elems.length; i++) {
34574           var zindex = Number.parseInt(
34575                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34576                 10
34577           );
34578           if (zindex > highest) {
34579             highest = zindex;
34580           }
34581         }
34582         
34583         
34584         
34585         this.el.dom.style.zIndex = highest;
34586         
34587         this.el.alignTo(this.bindEl, align[0],align[1]);
34588         //var arrow = this.el.select('.arrow',true).first();
34589         //arrow.set(align[2], 
34590         
34591         this.el.addClass(placement);
34592         this.el.addClass("bs-tooltip-"+ placement);
34593         
34594         this.el.addClass('in fade show');
34595         
34596         this.hoverState = null;
34597         
34598         if (this.el.hasClass('fade')) {
34599             // fade it?
34600         }
34601         
34602         
34603         
34604         
34605         
34606     },
34607     hide : function()
34608     {
34609          
34610         if (!this.el) {
34611             return;
34612         }
34613         //this.el.setXY([0,0]);
34614         if(this.bindEl.attr('tooltip-class')) {
34615             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34616         }
34617         this.el.removeClass(['show', 'in']);
34618         //this.el.hide();
34619         
34620     }
34621     
34622 });
34623  
34624
34625  /*
34626  * - LGPL
34627  *
34628  * Location Picker
34629  * 
34630  */
34631
34632 /**
34633  * @class Roo.bootstrap.LocationPicker
34634  * @extends Roo.bootstrap.Component
34635  * Bootstrap LocationPicker class
34636  * @cfg {Number} latitude Position when init default 0
34637  * @cfg {Number} longitude Position when init default 0
34638  * @cfg {Number} zoom default 15
34639  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34640  * @cfg {Boolean} mapTypeControl default false
34641  * @cfg {Boolean} disableDoubleClickZoom default false
34642  * @cfg {Boolean} scrollwheel default true
34643  * @cfg {Boolean} streetViewControl default false
34644  * @cfg {Number} radius default 0
34645  * @cfg {String} locationName
34646  * @cfg {Boolean} draggable default true
34647  * @cfg {Boolean} enableAutocomplete default false
34648  * @cfg {Boolean} enableReverseGeocode default true
34649  * @cfg {String} markerTitle
34650  * 
34651  * @constructor
34652  * Create a new LocationPicker
34653  * @param {Object} config The config object
34654  */
34655
34656
34657 Roo.bootstrap.LocationPicker = function(config){
34658     
34659     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34660     
34661     this.addEvents({
34662         /**
34663          * @event initial
34664          * Fires when the picker initialized.
34665          * @param {Roo.bootstrap.LocationPicker} this
34666          * @param {Google Location} location
34667          */
34668         initial : true,
34669         /**
34670          * @event positionchanged
34671          * Fires when the picker position changed.
34672          * @param {Roo.bootstrap.LocationPicker} this
34673          * @param {Google Location} location
34674          */
34675         positionchanged : true,
34676         /**
34677          * @event resize
34678          * Fires when the map resize.
34679          * @param {Roo.bootstrap.LocationPicker} this
34680          */
34681         resize : true,
34682         /**
34683          * @event show
34684          * Fires when the map show.
34685          * @param {Roo.bootstrap.LocationPicker} this
34686          */
34687         show : true,
34688         /**
34689          * @event hide
34690          * Fires when the map hide.
34691          * @param {Roo.bootstrap.LocationPicker} this
34692          */
34693         hide : true,
34694         /**
34695          * @event mapClick
34696          * Fires when click the map.
34697          * @param {Roo.bootstrap.LocationPicker} this
34698          * @param {Map event} e
34699          */
34700         mapClick : true,
34701         /**
34702          * @event mapRightClick
34703          * Fires when right click the map.
34704          * @param {Roo.bootstrap.LocationPicker} this
34705          * @param {Map event} e
34706          */
34707         mapRightClick : true,
34708         /**
34709          * @event markerClick
34710          * Fires when click the marker.
34711          * @param {Roo.bootstrap.LocationPicker} this
34712          * @param {Map event} e
34713          */
34714         markerClick : true,
34715         /**
34716          * @event markerRightClick
34717          * Fires when right click the marker.
34718          * @param {Roo.bootstrap.LocationPicker} this
34719          * @param {Map event} e
34720          */
34721         markerRightClick : true,
34722         /**
34723          * @event OverlayViewDraw
34724          * Fires when OverlayView Draw
34725          * @param {Roo.bootstrap.LocationPicker} this
34726          */
34727         OverlayViewDraw : true,
34728         /**
34729          * @event OverlayViewOnAdd
34730          * Fires when OverlayView Draw
34731          * @param {Roo.bootstrap.LocationPicker} this
34732          */
34733         OverlayViewOnAdd : true,
34734         /**
34735          * @event OverlayViewOnRemove
34736          * Fires when OverlayView Draw
34737          * @param {Roo.bootstrap.LocationPicker} this
34738          */
34739         OverlayViewOnRemove : true,
34740         /**
34741          * @event OverlayViewShow
34742          * Fires when OverlayView Draw
34743          * @param {Roo.bootstrap.LocationPicker} this
34744          * @param {Pixel} cpx
34745          */
34746         OverlayViewShow : true,
34747         /**
34748          * @event OverlayViewHide
34749          * Fires when OverlayView Draw
34750          * @param {Roo.bootstrap.LocationPicker} this
34751          */
34752         OverlayViewHide : true,
34753         /**
34754          * @event loadexception
34755          * Fires when load google lib failed.
34756          * @param {Roo.bootstrap.LocationPicker} this
34757          */
34758         loadexception : true
34759     });
34760         
34761 };
34762
34763 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34764     
34765     gMapContext: false,
34766     
34767     latitude: 0,
34768     longitude: 0,
34769     zoom: 15,
34770     mapTypeId: false,
34771     mapTypeControl: false,
34772     disableDoubleClickZoom: false,
34773     scrollwheel: true,
34774     streetViewControl: false,
34775     radius: 0,
34776     locationName: '',
34777     draggable: true,
34778     enableAutocomplete: false,
34779     enableReverseGeocode: true,
34780     markerTitle: '',
34781     
34782     getAutoCreate: function()
34783     {
34784
34785         var cfg = {
34786             tag: 'div',
34787             cls: 'roo-location-picker'
34788         };
34789         
34790         return cfg
34791     },
34792     
34793     initEvents: function(ct, position)
34794     {       
34795         if(!this.el.getWidth() || this.isApplied()){
34796             return;
34797         }
34798         
34799         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34800         
34801         this.initial();
34802     },
34803     
34804     initial: function()
34805     {
34806         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34807             this.fireEvent('loadexception', this);
34808             return;
34809         }
34810         
34811         if(!this.mapTypeId){
34812             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34813         }
34814         
34815         this.gMapContext = this.GMapContext();
34816         
34817         this.initOverlayView();
34818         
34819         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34820         
34821         var _this = this;
34822                 
34823         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34824             _this.setPosition(_this.gMapContext.marker.position);
34825         });
34826         
34827         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34828             _this.fireEvent('mapClick', this, event);
34829             
34830         });
34831
34832         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34833             _this.fireEvent('mapRightClick', this, event);
34834             
34835         });
34836         
34837         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34838             _this.fireEvent('markerClick', this, event);
34839             
34840         });
34841
34842         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34843             _this.fireEvent('markerRightClick', this, event);
34844             
34845         });
34846         
34847         this.setPosition(this.gMapContext.location);
34848         
34849         this.fireEvent('initial', this, this.gMapContext.location);
34850     },
34851     
34852     initOverlayView: function()
34853     {
34854         var _this = this;
34855         
34856         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34857             
34858             draw: function()
34859             {
34860                 _this.fireEvent('OverlayViewDraw', _this);
34861             },
34862             
34863             onAdd: function()
34864             {
34865                 _this.fireEvent('OverlayViewOnAdd', _this);
34866             },
34867             
34868             onRemove: function()
34869             {
34870                 _this.fireEvent('OverlayViewOnRemove', _this);
34871             },
34872             
34873             show: function(cpx)
34874             {
34875                 _this.fireEvent('OverlayViewShow', _this, cpx);
34876             },
34877             
34878             hide: function()
34879             {
34880                 _this.fireEvent('OverlayViewHide', _this);
34881             }
34882             
34883         });
34884     },
34885     
34886     fromLatLngToContainerPixel: function(event)
34887     {
34888         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34889     },
34890     
34891     isApplied: function() 
34892     {
34893         return this.getGmapContext() == false ? false : true;
34894     },
34895     
34896     getGmapContext: function() 
34897     {
34898         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34899     },
34900     
34901     GMapContext: function() 
34902     {
34903         var position = new google.maps.LatLng(this.latitude, this.longitude);
34904         
34905         var _map = new google.maps.Map(this.el.dom, {
34906             center: position,
34907             zoom: this.zoom,
34908             mapTypeId: this.mapTypeId,
34909             mapTypeControl: this.mapTypeControl,
34910             disableDoubleClickZoom: this.disableDoubleClickZoom,
34911             scrollwheel: this.scrollwheel,
34912             streetViewControl: this.streetViewControl,
34913             locationName: this.locationName,
34914             draggable: this.draggable,
34915             enableAutocomplete: this.enableAutocomplete,
34916             enableReverseGeocode: this.enableReverseGeocode
34917         });
34918         
34919         var _marker = new google.maps.Marker({
34920             position: position,
34921             map: _map,
34922             title: this.markerTitle,
34923             draggable: this.draggable
34924         });
34925         
34926         return {
34927             map: _map,
34928             marker: _marker,
34929             circle: null,
34930             location: position,
34931             radius: this.radius,
34932             locationName: this.locationName,
34933             addressComponents: {
34934                 formatted_address: null,
34935                 addressLine1: null,
34936                 addressLine2: null,
34937                 streetName: null,
34938                 streetNumber: null,
34939                 city: null,
34940                 district: null,
34941                 state: null,
34942                 stateOrProvince: null
34943             },
34944             settings: this,
34945             domContainer: this.el.dom,
34946             geodecoder: new google.maps.Geocoder()
34947         };
34948     },
34949     
34950     drawCircle: function(center, radius, options) 
34951     {
34952         if (this.gMapContext.circle != null) {
34953             this.gMapContext.circle.setMap(null);
34954         }
34955         if (radius > 0) {
34956             radius *= 1;
34957             options = Roo.apply({}, options, {
34958                 strokeColor: "#0000FF",
34959                 strokeOpacity: .35,
34960                 strokeWeight: 2,
34961                 fillColor: "#0000FF",
34962                 fillOpacity: .2
34963             });
34964             
34965             options.map = this.gMapContext.map;
34966             options.radius = radius;
34967             options.center = center;
34968             this.gMapContext.circle = new google.maps.Circle(options);
34969             return this.gMapContext.circle;
34970         }
34971         
34972         return null;
34973     },
34974     
34975     setPosition: function(location) 
34976     {
34977         this.gMapContext.location = location;
34978         this.gMapContext.marker.setPosition(location);
34979         this.gMapContext.map.panTo(location);
34980         this.drawCircle(location, this.gMapContext.radius, {});
34981         
34982         var _this = this;
34983         
34984         if (this.gMapContext.settings.enableReverseGeocode) {
34985             this.gMapContext.geodecoder.geocode({
34986                 latLng: this.gMapContext.location
34987             }, function(results, status) {
34988                 
34989                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34990                     _this.gMapContext.locationName = results[0].formatted_address;
34991                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34992                     
34993                     _this.fireEvent('positionchanged', this, location);
34994                 }
34995             });
34996             
34997             return;
34998         }
34999         
35000         this.fireEvent('positionchanged', this, location);
35001     },
35002     
35003     resize: function()
35004     {
35005         google.maps.event.trigger(this.gMapContext.map, "resize");
35006         
35007         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35008         
35009         this.fireEvent('resize', this);
35010     },
35011     
35012     setPositionByLatLng: function(latitude, longitude)
35013     {
35014         this.setPosition(new google.maps.LatLng(latitude, longitude));
35015     },
35016     
35017     getCurrentPosition: function() 
35018     {
35019         return {
35020             latitude: this.gMapContext.location.lat(),
35021             longitude: this.gMapContext.location.lng()
35022         };
35023     },
35024     
35025     getAddressName: function() 
35026     {
35027         return this.gMapContext.locationName;
35028     },
35029     
35030     getAddressComponents: function() 
35031     {
35032         return this.gMapContext.addressComponents;
35033     },
35034     
35035     address_component_from_google_geocode: function(address_components) 
35036     {
35037         var result = {};
35038         
35039         for (var i = 0; i < address_components.length; i++) {
35040             var component = address_components[i];
35041             if (component.types.indexOf("postal_code") >= 0) {
35042                 result.postalCode = component.short_name;
35043             } else if (component.types.indexOf("street_number") >= 0) {
35044                 result.streetNumber = component.short_name;
35045             } else if (component.types.indexOf("route") >= 0) {
35046                 result.streetName = component.short_name;
35047             } else if (component.types.indexOf("neighborhood") >= 0) {
35048                 result.city = component.short_name;
35049             } else if (component.types.indexOf("locality") >= 0) {
35050                 result.city = component.short_name;
35051             } else if (component.types.indexOf("sublocality") >= 0) {
35052                 result.district = component.short_name;
35053             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35054                 result.stateOrProvince = component.short_name;
35055             } else if (component.types.indexOf("country") >= 0) {
35056                 result.country = component.short_name;
35057             }
35058         }
35059         
35060         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35061         result.addressLine2 = "";
35062         return result;
35063     },
35064     
35065     setZoomLevel: function(zoom)
35066     {
35067         this.gMapContext.map.setZoom(zoom);
35068     },
35069     
35070     show: function()
35071     {
35072         if(!this.el){
35073             return;
35074         }
35075         
35076         this.el.show();
35077         
35078         this.resize();
35079         
35080         this.fireEvent('show', this);
35081     },
35082     
35083     hide: function()
35084     {
35085         if(!this.el){
35086             return;
35087         }
35088         
35089         this.el.hide();
35090         
35091         this.fireEvent('hide', this);
35092     }
35093     
35094 });
35095
35096 Roo.apply(Roo.bootstrap.LocationPicker, {
35097     
35098     OverlayView : function(map, options)
35099     {
35100         options = options || {};
35101         
35102         this.setMap(map);
35103     }
35104     
35105     
35106 });/**
35107  * @class Roo.bootstrap.Alert
35108  * @extends Roo.bootstrap.Component
35109  * Bootstrap Alert class - shows an alert area box
35110  * eg
35111  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35112   Enter a valid email address
35113 </div>
35114  * @licence LGPL
35115  * @cfg {String} title The title of alert
35116  * @cfg {String} html The content of alert
35117  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35118  * @cfg {String} fa font-awesomeicon
35119  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35120  * @cfg {Boolean} close true to show a x closer
35121  * 
35122  * 
35123  * @constructor
35124  * Create a new alert
35125  * @param {Object} config The config object
35126  */
35127
35128
35129 Roo.bootstrap.Alert = function(config){
35130     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35131     
35132 };
35133
35134 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35135     
35136     title: '',
35137     html: '',
35138     weight: false,
35139     fa: false,
35140     faicon: false, // BC
35141     close : false,
35142     
35143     
35144     getAutoCreate : function()
35145     {
35146         
35147         var cfg = {
35148             tag : 'div',
35149             cls : 'alert',
35150             cn : [
35151                 {
35152                     tag: 'button',
35153                     type :  "button",
35154                     cls: "close",
35155                     html : '×',
35156                     style : this.close ? '' : 'display:none'
35157                 },
35158                 {
35159                     tag : 'i',
35160                     cls : 'roo-alert-icon'
35161                     
35162                 },
35163                 {
35164                     tag : 'b',
35165                     cls : 'roo-alert-title',
35166                     html : this.title
35167                 },
35168                 {
35169                     tag : 'span',
35170                     cls : 'roo-alert-text',
35171                     html : this.html
35172                 }
35173             ]
35174         };
35175         
35176         if(this.faicon){
35177             cfg.cn[0].cls += ' fa ' + this.faicon;
35178         }
35179         if(this.fa){
35180             cfg.cn[0].cls += ' fa ' + this.fa;
35181         }
35182         
35183         if(this.weight){
35184             cfg.cls += ' alert-' + this.weight;
35185         }
35186         
35187         return cfg;
35188     },
35189     
35190     initEvents: function() 
35191     {
35192         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35193         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35194         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35195         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35196         if (this.seconds > 0) {
35197             this.hide.defer(this.seconds, this);
35198         }
35199     },
35200     /**
35201      * Set the Title Message HTML
35202      * @param {String} html
35203      */
35204     setTitle : function(str)
35205     {
35206         this.titleEl.dom.innerHTML = str;
35207     },
35208      
35209      /**
35210      * Set the Body Message HTML
35211      * @param {String} html
35212      */
35213     setHtml : function(str)
35214     {
35215         this.htmlEl.dom.innerHTML = str;
35216     },
35217     /**
35218      * Set the Weight of the alert
35219      * @param {String} (success|info|warning|danger) weight
35220      */
35221     
35222     setWeight : function(weight)
35223     {
35224         if(this.weight){
35225             this.el.removeClass('alert-' + this.weight);
35226         }
35227         
35228         this.weight = weight;
35229         
35230         this.el.addClass('alert-' + this.weight);
35231     },
35232       /**
35233      * Set the Icon of the alert
35234      * @param {String} see fontawsome names (name without the 'fa-' bit)
35235      */
35236     setIcon : function(icon)
35237     {
35238         if(this.faicon){
35239             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35240         }
35241         
35242         this.faicon = icon;
35243         
35244         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35245     },
35246     /**
35247      * Hide the Alert
35248      */
35249     hide: function() 
35250     {
35251         this.el.hide();   
35252     },
35253     /**
35254      * Show the Alert
35255      */
35256     show: function() 
35257     {  
35258         this.el.show();   
35259     }
35260     
35261 });
35262
35263  
35264 /*
35265 * Licence: LGPL
35266 */
35267
35268 /**
35269  * @class Roo.bootstrap.UploadCropbox
35270  * @extends Roo.bootstrap.Component
35271  * Bootstrap UploadCropbox class
35272  * @cfg {String} emptyText show when image has been loaded
35273  * @cfg {String} rotateNotify show when image too small to rotate
35274  * @cfg {Number} errorTimeout default 3000
35275  * @cfg {Number} minWidth default 300
35276  * @cfg {Number} minHeight default 300
35277  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35278  * @cfg {Boolean} isDocument (true|false) default false
35279  * @cfg {String} url action url
35280  * @cfg {String} paramName default 'imageUpload'
35281  * @cfg {String} method default POST
35282  * @cfg {Boolean} loadMask (true|false) default true
35283  * @cfg {Boolean} loadingText default 'Loading...'
35284  * 
35285  * @constructor
35286  * Create a new UploadCropbox
35287  * @param {Object} config The config object
35288  */
35289
35290 Roo.bootstrap.UploadCropbox = function(config){
35291     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35292     
35293     this.addEvents({
35294         /**
35295          * @event beforeselectfile
35296          * Fire before select file
35297          * @param {Roo.bootstrap.UploadCropbox} this
35298          */
35299         "beforeselectfile" : true,
35300         /**
35301          * @event initial
35302          * Fire after initEvent
35303          * @param {Roo.bootstrap.UploadCropbox} this
35304          */
35305         "initial" : true,
35306         /**
35307          * @event crop
35308          * Fire after initEvent
35309          * @param {Roo.bootstrap.UploadCropbox} this
35310          * @param {String} data
35311          */
35312         "crop" : true,
35313         /**
35314          * @event prepare
35315          * Fire when preparing the file data
35316          * @param {Roo.bootstrap.UploadCropbox} this
35317          * @param {Object} file
35318          */
35319         "prepare" : true,
35320         /**
35321          * @event exception
35322          * Fire when get exception
35323          * @param {Roo.bootstrap.UploadCropbox} this
35324          * @param {XMLHttpRequest} xhr
35325          */
35326         "exception" : true,
35327         /**
35328          * @event beforeloadcanvas
35329          * Fire before load the canvas
35330          * @param {Roo.bootstrap.UploadCropbox} this
35331          * @param {String} src
35332          */
35333         "beforeloadcanvas" : true,
35334         /**
35335          * @event trash
35336          * Fire when trash image
35337          * @param {Roo.bootstrap.UploadCropbox} this
35338          */
35339         "trash" : true,
35340         /**
35341          * @event download
35342          * Fire when download the image
35343          * @param {Roo.bootstrap.UploadCropbox} this
35344          */
35345         "download" : true,
35346         /**
35347          * @event footerbuttonclick
35348          * Fire when footerbuttonclick
35349          * @param {Roo.bootstrap.UploadCropbox} this
35350          * @param {String} type
35351          */
35352         "footerbuttonclick" : true,
35353         /**
35354          * @event resize
35355          * Fire when resize
35356          * @param {Roo.bootstrap.UploadCropbox} this
35357          */
35358         "resize" : true,
35359         /**
35360          * @event rotate
35361          * Fire when rotate the image
35362          * @param {Roo.bootstrap.UploadCropbox} this
35363          * @param {String} pos
35364          */
35365         "rotate" : true,
35366         /**
35367          * @event inspect
35368          * Fire when inspect the file
35369          * @param {Roo.bootstrap.UploadCropbox} this
35370          * @param {Object} file
35371          */
35372         "inspect" : true,
35373         /**
35374          * @event upload
35375          * Fire when xhr upload the file
35376          * @param {Roo.bootstrap.UploadCropbox} this
35377          * @param {Object} data
35378          */
35379         "upload" : true,
35380         /**
35381          * @event arrange
35382          * Fire when arrange the file data
35383          * @param {Roo.bootstrap.UploadCropbox} this
35384          * @param {Object} formData
35385          */
35386         "arrange" : true
35387     });
35388     
35389     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35390 };
35391
35392 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35393     
35394     emptyText : 'Click to upload image',
35395     rotateNotify : 'Image is too small to rotate',
35396     errorTimeout : 3000,
35397     scale : 0,
35398     baseScale : 1,
35399     rotate : 0,
35400     dragable : false,
35401     pinching : false,
35402     mouseX : 0,
35403     mouseY : 0,
35404     cropData : false,
35405     minWidth : 300,
35406     minHeight : 300,
35407     file : false,
35408     exif : {},
35409     baseRotate : 1,
35410     cropType : 'image/jpeg',
35411     buttons : false,
35412     canvasLoaded : false,
35413     isDocument : false,
35414     method : 'POST',
35415     paramName : 'imageUpload',
35416     loadMask : true,
35417     loadingText : 'Loading...',
35418     maskEl : false,
35419     
35420     getAutoCreate : function()
35421     {
35422         var cfg = {
35423             tag : 'div',
35424             cls : 'roo-upload-cropbox',
35425             cn : [
35426                 {
35427                     tag : 'input',
35428                     cls : 'roo-upload-cropbox-selector',
35429                     type : 'file'
35430                 },
35431                 {
35432                     tag : 'div',
35433                     cls : 'roo-upload-cropbox-body',
35434                     style : 'cursor:pointer',
35435                     cn : [
35436                         {
35437                             tag : 'div',
35438                             cls : 'roo-upload-cropbox-preview'
35439                         },
35440                         {
35441                             tag : 'div',
35442                             cls : 'roo-upload-cropbox-thumb'
35443                         },
35444                         {
35445                             tag : 'div',
35446                             cls : 'roo-upload-cropbox-empty-notify',
35447                             html : this.emptyText
35448                         },
35449                         {
35450                             tag : 'div',
35451                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35452                             html : this.rotateNotify
35453                         }
35454                     ]
35455                 },
35456                 {
35457                     tag : 'div',
35458                     cls : 'roo-upload-cropbox-footer',
35459                     cn : {
35460                         tag : 'div',
35461                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35462                         cn : []
35463                     }
35464                 }
35465             ]
35466         };
35467         
35468         return cfg;
35469     },
35470     
35471     onRender : function(ct, position)
35472     {
35473         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35474         
35475         if (this.buttons.length) {
35476             
35477             Roo.each(this.buttons, function(bb) {
35478                 
35479                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35480                 
35481                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35482                 
35483             }, this);
35484         }
35485         
35486         if(this.loadMask){
35487             this.maskEl = this.el;
35488         }
35489     },
35490     
35491     initEvents : function()
35492     {
35493         this.urlAPI = (window.createObjectURL && window) || 
35494                                 (window.URL && URL.revokeObjectURL && URL) || 
35495                                 (window.webkitURL && webkitURL);
35496                         
35497         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35498         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35499         
35500         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35501         this.selectorEl.hide();
35502         
35503         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35504         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35505         
35506         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35507         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35508         this.thumbEl.hide();
35509         
35510         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35511         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35512         
35513         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35514         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35515         this.errorEl.hide();
35516         
35517         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35518         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35519         this.footerEl.hide();
35520         
35521         this.setThumbBoxSize();
35522         
35523         this.bind();
35524         
35525         this.resize();
35526         
35527         this.fireEvent('initial', this);
35528     },
35529
35530     bind : function()
35531     {
35532         var _this = this;
35533         
35534         window.addEventListener("resize", function() { _this.resize(); } );
35535         
35536         this.bodyEl.on('click', this.beforeSelectFile, this);
35537         
35538         if(Roo.isTouch){
35539             this.bodyEl.on('touchstart', this.onTouchStart, this);
35540             this.bodyEl.on('touchmove', this.onTouchMove, this);
35541             this.bodyEl.on('touchend', this.onTouchEnd, this);
35542         }
35543         
35544         if(!Roo.isTouch){
35545             this.bodyEl.on('mousedown', this.onMouseDown, this);
35546             this.bodyEl.on('mousemove', this.onMouseMove, this);
35547             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35548             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35549             Roo.get(document).on('mouseup', this.onMouseUp, this);
35550         }
35551         
35552         this.selectorEl.on('change', this.onFileSelected, this);
35553     },
35554     
35555     reset : function()
35556     {    
35557         this.scale = 0;
35558         this.baseScale = 1;
35559         this.rotate = 0;
35560         this.baseRotate = 1;
35561         this.dragable = false;
35562         this.pinching = false;
35563         this.mouseX = 0;
35564         this.mouseY = 0;
35565         this.cropData = false;
35566         this.notifyEl.dom.innerHTML = this.emptyText;
35567         
35568         this.selectorEl.dom.value = '';
35569         
35570     },
35571     
35572     resize : function()
35573     {
35574         if(this.fireEvent('resize', this) != false){
35575             this.setThumbBoxPosition();
35576             this.setCanvasPosition();
35577         }
35578     },
35579     
35580     onFooterButtonClick : function(e, el, o, type)
35581     {
35582         switch (type) {
35583             case 'rotate-left' :
35584                 this.onRotateLeft(e);
35585                 break;
35586             case 'rotate-right' :
35587                 this.onRotateRight(e);
35588                 break;
35589             case 'picture' :
35590                 this.beforeSelectFile(e);
35591                 break;
35592             case 'trash' :
35593                 this.trash(e);
35594                 break;
35595             case 'crop' :
35596                 this.crop(e);
35597                 break;
35598             case 'download' :
35599                 this.download(e);
35600                 break;
35601             default :
35602                 break;
35603         }
35604         
35605         this.fireEvent('footerbuttonclick', this, type);
35606     },
35607     
35608     beforeSelectFile : function(e)
35609     {
35610         e.preventDefault();
35611         
35612         if(this.fireEvent('beforeselectfile', this) != false){
35613             this.selectorEl.dom.click();
35614         }
35615     },
35616     
35617     onFileSelected : function(e)
35618     {
35619         e.preventDefault();
35620         
35621         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35622             return;
35623         }
35624         
35625         var file = this.selectorEl.dom.files[0];
35626         
35627         if(this.fireEvent('inspect', this, file) != false){
35628             this.prepare(file);
35629         }
35630         
35631     },
35632     
35633     trash : function(e)
35634     {
35635         this.fireEvent('trash', this);
35636     },
35637     
35638     download : function(e)
35639     {
35640         this.fireEvent('download', this);
35641     },
35642     
35643     loadCanvas : function(src)
35644     {   
35645         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35646             
35647             this.reset();
35648             
35649             this.imageEl = document.createElement('img');
35650             
35651             var _this = this;
35652             
35653             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35654             
35655             this.imageEl.src = src;
35656         }
35657     },
35658     
35659     onLoadCanvas : function()
35660     {   
35661         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35662         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35663         
35664         this.bodyEl.un('click', this.beforeSelectFile, this);
35665         
35666         this.notifyEl.hide();
35667         this.thumbEl.show();
35668         this.footerEl.show();
35669         
35670         this.baseRotateLevel();
35671         
35672         if(this.isDocument){
35673             this.setThumbBoxSize();
35674         }
35675         
35676         this.setThumbBoxPosition();
35677         
35678         this.baseScaleLevel();
35679         
35680         this.draw();
35681         
35682         this.resize();
35683         
35684         this.canvasLoaded = true;
35685         
35686         if(this.loadMask){
35687             this.maskEl.unmask();
35688         }
35689         
35690     },
35691     
35692     setCanvasPosition : function()
35693     {   
35694         if(!this.canvasEl){
35695             return;
35696         }
35697         
35698         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35699         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35700         
35701         this.previewEl.setLeft(pw);
35702         this.previewEl.setTop(ph);
35703         
35704     },
35705     
35706     onMouseDown : function(e)
35707     {   
35708         e.stopEvent();
35709         
35710         this.dragable = true;
35711         this.pinching = false;
35712         
35713         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35714             this.dragable = false;
35715             return;
35716         }
35717         
35718         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35719         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35720         
35721     },
35722     
35723     onMouseMove : function(e)
35724     {   
35725         e.stopEvent();
35726         
35727         if(!this.canvasLoaded){
35728             return;
35729         }
35730         
35731         if (!this.dragable){
35732             return;
35733         }
35734         
35735         var minX = Math.ceil(this.thumbEl.getLeft(true));
35736         var minY = Math.ceil(this.thumbEl.getTop(true));
35737         
35738         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35739         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35740         
35741         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35742         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35743         
35744         x = x - this.mouseX;
35745         y = y - this.mouseY;
35746         
35747         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35748         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35749         
35750         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35751         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35752         
35753         this.previewEl.setLeft(bgX);
35754         this.previewEl.setTop(bgY);
35755         
35756         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35757         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35758     },
35759     
35760     onMouseUp : function(e)
35761     {   
35762         e.stopEvent();
35763         
35764         this.dragable = false;
35765     },
35766     
35767     onMouseWheel : function(e)
35768     {   
35769         e.stopEvent();
35770         
35771         this.startScale = this.scale;
35772         
35773         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35774         
35775         if(!this.zoomable()){
35776             this.scale = this.startScale;
35777             return;
35778         }
35779         
35780         this.draw();
35781         
35782         return;
35783     },
35784     
35785     zoomable : function()
35786     {
35787         var minScale = this.thumbEl.getWidth() / this.minWidth;
35788         
35789         if(this.minWidth < this.minHeight){
35790             minScale = this.thumbEl.getHeight() / this.minHeight;
35791         }
35792         
35793         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35794         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35795         
35796         if(
35797                 this.isDocument &&
35798                 (this.rotate == 0 || this.rotate == 180) && 
35799                 (
35800                     width > this.imageEl.OriginWidth || 
35801                     height > this.imageEl.OriginHeight ||
35802                     (width < this.minWidth && height < this.minHeight)
35803                 )
35804         ){
35805             return false;
35806         }
35807         
35808         if(
35809                 this.isDocument &&
35810                 (this.rotate == 90 || this.rotate == 270) && 
35811                 (
35812                     width > this.imageEl.OriginWidth || 
35813                     height > this.imageEl.OriginHeight ||
35814                     (width < this.minHeight && height < this.minWidth)
35815                 )
35816         ){
35817             return false;
35818         }
35819         
35820         if(
35821                 !this.isDocument &&
35822                 (this.rotate == 0 || this.rotate == 180) && 
35823                 (
35824                     width < this.minWidth || 
35825                     width > this.imageEl.OriginWidth || 
35826                     height < this.minHeight || 
35827                     height > this.imageEl.OriginHeight
35828                 )
35829         ){
35830             return false;
35831         }
35832         
35833         if(
35834                 !this.isDocument &&
35835                 (this.rotate == 90 || this.rotate == 270) && 
35836                 (
35837                     width < this.minHeight || 
35838                     width > this.imageEl.OriginWidth || 
35839                     height < this.minWidth || 
35840                     height > this.imageEl.OriginHeight
35841                 )
35842         ){
35843             return false;
35844         }
35845         
35846         return true;
35847         
35848     },
35849     
35850     onRotateLeft : function(e)
35851     {   
35852         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35853             
35854             var minScale = this.thumbEl.getWidth() / this.minWidth;
35855             
35856             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35857             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35858             
35859             this.startScale = this.scale;
35860             
35861             while (this.getScaleLevel() < minScale){
35862             
35863                 this.scale = this.scale + 1;
35864                 
35865                 if(!this.zoomable()){
35866                     break;
35867                 }
35868                 
35869                 if(
35870                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35871                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35872                 ){
35873                     continue;
35874                 }
35875                 
35876                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35877
35878                 this.draw();
35879                 
35880                 return;
35881             }
35882             
35883             this.scale = this.startScale;
35884             
35885             this.onRotateFail();
35886             
35887             return false;
35888         }
35889         
35890         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35891
35892         if(this.isDocument){
35893             this.setThumbBoxSize();
35894             this.setThumbBoxPosition();
35895             this.setCanvasPosition();
35896         }
35897         
35898         this.draw();
35899         
35900         this.fireEvent('rotate', this, 'left');
35901         
35902     },
35903     
35904     onRotateRight : function(e)
35905     {
35906         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35907             
35908             var minScale = this.thumbEl.getWidth() / this.minWidth;
35909         
35910             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35911             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35912             
35913             this.startScale = this.scale;
35914             
35915             while (this.getScaleLevel() < minScale){
35916             
35917                 this.scale = this.scale + 1;
35918                 
35919                 if(!this.zoomable()){
35920                     break;
35921                 }
35922                 
35923                 if(
35924                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35925                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35926                 ){
35927                     continue;
35928                 }
35929                 
35930                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35931
35932                 this.draw();
35933                 
35934                 return;
35935             }
35936             
35937             this.scale = this.startScale;
35938             
35939             this.onRotateFail();
35940             
35941             return false;
35942         }
35943         
35944         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35945
35946         if(this.isDocument){
35947             this.setThumbBoxSize();
35948             this.setThumbBoxPosition();
35949             this.setCanvasPosition();
35950         }
35951         
35952         this.draw();
35953         
35954         this.fireEvent('rotate', this, 'right');
35955     },
35956     
35957     onRotateFail : function()
35958     {
35959         this.errorEl.show(true);
35960         
35961         var _this = this;
35962         
35963         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35964     },
35965     
35966     draw : function()
35967     {
35968         this.previewEl.dom.innerHTML = '';
35969         
35970         var canvasEl = document.createElement("canvas");
35971         
35972         var contextEl = canvasEl.getContext("2d");
35973         
35974         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35975         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35976         var center = this.imageEl.OriginWidth / 2;
35977         
35978         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35979             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35980             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35981             center = this.imageEl.OriginHeight / 2;
35982         }
35983         
35984         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35985         
35986         contextEl.translate(center, center);
35987         contextEl.rotate(this.rotate * Math.PI / 180);
35988
35989         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35990         
35991         this.canvasEl = document.createElement("canvas");
35992         
35993         this.contextEl = this.canvasEl.getContext("2d");
35994         
35995         switch (this.rotate) {
35996             case 0 :
35997                 
35998                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35999                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36000                 
36001                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36002                 
36003                 break;
36004             case 90 : 
36005                 
36006                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36007                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36008                 
36009                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36010                     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);
36011                     break;
36012                 }
36013                 
36014                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36015                 
36016                 break;
36017             case 180 :
36018                 
36019                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36020                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36021                 
36022                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36023                     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);
36024                     break;
36025                 }
36026                 
36027                 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);
36028                 
36029                 break;
36030             case 270 :
36031                 
36032                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36033                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36034         
36035                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36036                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36037                     break;
36038                 }
36039                 
36040                 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);
36041                 
36042                 break;
36043             default : 
36044                 break;
36045         }
36046         
36047         this.previewEl.appendChild(this.canvasEl);
36048         
36049         this.setCanvasPosition();
36050     },
36051     
36052     crop : function()
36053     {
36054         if(!this.canvasLoaded){
36055             return;
36056         }
36057         
36058         var imageCanvas = document.createElement("canvas");
36059         
36060         var imageContext = imageCanvas.getContext("2d");
36061         
36062         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36063         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36064         
36065         var center = imageCanvas.width / 2;
36066         
36067         imageContext.translate(center, center);
36068         
36069         imageContext.rotate(this.rotate * Math.PI / 180);
36070         
36071         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36072         
36073         var canvas = document.createElement("canvas");
36074         
36075         var context = canvas.getContext("2d");
36076                 
36077         canvas.width = this.minWidth;
36078         canvas.height = this.minHeight;
36079
36080         switch (this.rotate) {
36081             case 0 :
36082                 
36083                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36084                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36085                 
36086                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36087                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36088                 
36089                 var targetWidth = this.minWidth - 2 * x;
36090                 var targetHeight = this.minHeight - 2 * y;
36091                 
36092                 var scale = 1;
36093                 
36094                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36095                     scale = targetWidth / width;
36096                 }
36097                 
36098                 if(x > 0 && y == 0){
36099                     scale = targetHeight / height;
36100                 }
36101                 
36102                 if(x > 0 && y > 0){
36103                     scale = targetWidth / width;
36104                     
36105                     if(width < height){
36106                         scale = targetHeight / height;
36107                     }
36108                 }
36109                 
36110                 context.scale(scale, scale);
36111                 
36112                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36113                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36114
36115                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36116                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36117
36118                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36119                 
36120                 break;
36121             case 90 : 
36122                 
36123                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36124                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36125                 
36126                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36127                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36128                 
36129                 var targetWidth = this.minWidth - 2 * x;
36130                 var targetHeight = this.minHeight - 2 * y;
36131                 
36132                 var scale = 1;
36133                 
36134                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36135                     scale = targetWidth / width;
36136                 }
36137                 
36138                 if(x > 0 && y == 0){
36139                     scale = targetHeight / height;
36140                 }
36141                 
36142                 if(x > 0 && y > 0){
36143                     scale = targetWidth / width;
36144                     
36145                     if(width < height){
36146                         scale = targetHeight / height;
36147                     }
36148                 }
36149                 
36150                 context.scale(scale, scale);
36151                 
36152                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36153                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36154
36155                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36156                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36157                 
36158                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36159                 
36160                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36161                 
36162                 break;
36163             case 180 :
36164                 
36165                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36166                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36167                 
36168                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36169                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36170                 
36171                 var targetWidth = this.minWidth - 2 * x;
36172                 var targetHeight = this.minHeight - 2 * y;
36173                 
36174                 var scale = 1;
36175                 
36176                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36177                     scale = targetWidth / width;
36178                 }
36179                 
36180                 if(x > 0 && y == 0){
36181                     scale = targetHeight / height;
36182                 }
36183                 
36184                 if(x > 0 && y > 0){
36185                     scale = targetWidth / width;
36186                     
36187                     if(width < height){
36188                         scale = targetHeight / height;
36189                     }
36190                 }
36191                 
36192                 context.scale(scale, scale);
36193                 
36194                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36195                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36196
36197                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36198                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36199
36200                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36201                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36202                 
36203                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36204                 
36205                 break;
36206             case 270 :
36207                 
36208                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36209                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36210                 
36211                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36212                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36213                 
36214                 var targetWidth = this.minWidth - 2 * x;
36215                 var targetHeight = this.minHeight - 2 * y;
36216                 
36217                 var scale = 1;
36218                 
36219                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36220                     scale = targetWidth / width;
36221                 }
36222                 
36223                 if(x > 0 && y == 0){
36224                     scale = targetHeight / height;
36225                 }
36226                 
36227                 if(x > 0 && y > 0){
36228                     scale = targetWidth / width;
36229                     
36230                     if(width < height){
36231                         scale = targetHeight / height;
36232                     }
36233                 }
36234                 
36235                 context.scale(scale, scale);
36236                 
36237                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36238                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36239
36240                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36241                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36242                 
36243                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36244                 
36245                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36246                 
36247                 break;
36248             default : 
36249                 break;
36250         }
36251         
36252         this.cropData = canvas.toDataURL(this.cropType);
36253         
36254         if(this.fireEvent('crop', this, this.cropData) !== false){
36255             this.process(this.file, this.cropData);
36256         }
36257         
36258         return;
36259         
36260     },
36261     
36262     setThumbBoxSize : function()
36263     {
36264         var width, height;
36265         
36266         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36267             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36268             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36269             
36270             this.minWidth = width;
36271             this.minHeight = height;
36272             
36273             if(this.rotate == 90 || this.rotate == 270){
36274                 this.minWidth = height;
36275                 this.minHeight = width;
36276             }
36277         }
36278         
36279         height = 300;
36280         width = Math.ceil(this.minWidth * height / this.minHeight);
36281         
36282         if(this.minWidth > this.minHeight){
36283             width = 300;
36284             height = Math.ceil(this.minHeight * width / this.minWidth);
36285         }
36286         
36287         this.thumbEl.setStyle({
36288             width : width + 'px',
36289             height : height + 'px'
36290         });
36291
36292         return;
36293             
36294     },
36295     
36296     setThumbBoxPosition : function()
36297     {
36298         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36299         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36300         
36301         this.thumbEl.setLeft(x);
36302         this.thumbEl.setTop(y);
36303         
36304     },
36305     
36306     baseRotateLevel : function()
36307     {
36308         this.baseRotate = 1;
36309         
36310         if(
36311                 typeof(this.exif) != 'undefined' &&
36312                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36313                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36314         ){
36315             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36316         }
36317         
36318         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36319         
36320     },
36321     
36322     baseScaleLevel : function()
36323     {
36324         var width, height;
36325         
36326         if(this.isDocument){
36327             
36328             if(this.baseRotate == 6 || this.baseRotate == 8){
36329             
36330                 height = this.thumbEl.getHeight();
36331                 this.baseScale = height / this.imageEl.OriginWidth;
36332
36333                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36334                     width = this.thumbEl.getWidth();
36335                     this.baseScale = width / this.imageEl.OriginHeight;
36336                 }
36337
36338                 return;
36339             }
36340
36341             height = this.thumbEl.getHeight();
36342             this.baseScale = height / this.imageEl.OriginHeight;
36343
36344             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36345                 width = this.thumbEl.getWidth();
36346                 this.baseScale = width / this.imageEl.OriginWidth;
36347             }
36348
36349             return;
36350         }
36351         
36352         if(this.baseRotate == 6 || this.baseRotate == 8){
36353             
36354             width = this.thumbEl.getHeight();
36355             this.baseScale = width / this.imageEl.OriginHeight;
36356             
36357             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36358                 height = this.thumbEl.getWidth();
36359                 this.baseScale = height / this.imageEl.OriginHeight;
36360             }
36361             
36362             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36363                 height = this.thumbEl.getWidth();
36364                 this.baseScale = height / this.imageEl.OriginHeight;
36365                 
36366                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36367                     width = this.thumbEl.getHeight();
36368                     this.baseScale = width / this.imageEl.OriginWidth;
36369                 }
36370             }
36371             
36372             return;
36373         }
36374         
36375         width = this.thumbEl.getWidth();
36376         this.baseScale = width / this.imageEl.OriginWidth;
36377         
36378         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36379             height = this.thumbEl.getHeight();
36380             this.baseScale = height / this.imageEl.OriginHeight;
36381         }
36382         
36383         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36384             
36385             height = this.thumbEl.getHeight();
36386             this.baseScale = height / this.imageEl.OriginHeight;
36387             
36388             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36389                 width = this.thumbEl.getWidth();
36390                 this.baseScale = width / this.imageEl.OriginWidth;
36391             }
36392             
36393         }
36394         
36395         return;
36396     },
36397     
36398     getScaleLevel : function()
36399     {
36400         return this.baseScale * Math.pow(1.1, this.scale);
36401     },
36402     
36403     onTouchStart : function(e)
36404     {
36405         if(!this.canvasLoaded){
36406             this.beforeSelectFile(e);
36407             return;
36408         }
36409         
36410         var touches = e.browserEvent.touches;
36411         
36412         if(!touches){
36413             return;
36414         }
36415         
36416         if(touches.length == 1){
36417             this.onMouseDown(e);
36418             return;
36419         }
36420         
36421         if(touches.length != 2){
36422             return;
36423         }
36424         
36425         var coords = [];
36426         
36427         for(var i = 0, finger; finger = touches[i]; i++){
36428             coords.push(finger.pageX, finger.pageY);
36429         }
36430         
36431         var x = Math.pow(coords[0] - coords[2], 2);
36432         var y = Math.pow(coords[1] - coords[3], 2);
36433         
36434         this.startDistance = Math.sqrt(x + y);
36435         
36436         this.startScale = this.scale;
36437         
36438         this.pinching = true;
36439         this.dragable = false;
36440         
36441     },
36442     
36443     onTouchMove : function(e)
36444     {
36445         if(!this.pinching && !this.dragable){
36446             return;
36447         }
36448         
36449         var touches = e.browserEvent.touches;
36450         
36451         if(!touches){
36452             return;
36453         }
36454         
36455         if(this.dragable){
36456             this.onMouseMove(e);
36457             return;
36458         }
36459         
36460         var coords = [];
36461         
36462         for(var i = 0, finger; finger = touches[i]; i++){
36463             coords.push(finger.pageX, finger.pageY);
36464         }
36465         
36466         var x = Math.pow(coords[0] - coords[2], 2);
36467         var y = Math.pow(coords[1] - coords[3], 2);
36468         
36469         this.endDistance = Math.sqrt(x + y);
36470         
36471         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36472         
36473         if(!this.zoomable()){
36474             this.scale = this.startScale;
36475             return;
36476         }
36477         
36478         this.draw();
36479         
36480     },
36481     
36482     onTouchEnd : function(e)
36483     {
36484         this.pinching = false;
36485         this.dragable = false;
36486         
36487     },
36488     
36489     process : function(file, crop)
36490     {
36491         if(this.loadMask){
36492             this.maskEl.mask(this.loadingText);
36493         }
36494         
36495         this.xhr = new XMLHttpRequest();
36496         
36497         file.xhr = this.xhr;
36498
36499         this.xhr.open(this.method, this.url, true);
36500         
36501         var headers = {
36502             "Accept": "application/json",
36503             "Cache-Control": "no-cache",
36504             "X-Requested-With": "XMLHttpRequest"
36505         };
36506         
36507         for (var headerName in headers) {
36508             var headerValue = headers[headerName];
36509             if (headerValue) {
36510                 this.xhr.setRequestHeader(headerName, headerValue);
36511             }
36512         }
36513         
36514         var _this = this;
36515         
36516         this.xhr.onload = function()
36517         {
36518             _this.xhrOnLoad(_this.xhr);
36519         }
36520         
36521         this.xhr.onerror = function()
36522         {
36523             _this.xhrOnError(_this.xhr);
36524         }
36525         
36526         var formData = new FormData();
36527
36528         formData.append('returnHTML', 'NO');
36529         
36530         if(crop){
36531             formData.append('crop', crop);
36532         }
36533         
36534         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36535             formData.append(this.paramName, file, file.name);
36536         }
36537         
36538         if(typeof(file.filename) != 'undefined'){
36539             formData.append('filename', file.filename);
36540         }
36541         
36542         if(typeof(file.mimetype) != 'undefined'){
36543             formData.append('mimetype', file.mimetype);
36544         }
36545         
36546         if(this.fireEvent('arrange', this, formData) != false){
36547             this.xhr.send(formData);
36548         };
36549     },
36550     
36551     xhrOnLoad : function(xhr)
36552     {
36553         if(this.loadMask){
36554             this.maskEl.unmask();
36555         }
36556         
36557         if (xhr.readyState !== 4) {
36558             this.fireEvent('exception', this, xhr);
36559             return;
36560         }
36561
36562         var response = Roo.decode(xhr.responseText);
36563         
36564         if(!response.success){
36565             this.fireEvent('exception', this, xhr);
36566             return;
36567         }
36568         
36569         var response = Roo.decode(xhr.responseText);
36570         
36571         this.fireEvent('upload', this, response);
36572         
36573     },
36574     
36575     xhrOnError : function()
36576     {
36577         if(this.loadMask){
36578             this.maskEl.unmask();
36579         }
36580         
36581         Roo.log('xhr on error');
36582         
36583         var response = Roo.decode(xhr.responseText);
36584           
36585         Roo.log(response);
36586         
36587     },
36588     
36589     prepare : function(file)
36590     {   
36591         if(this.loadMask){
36592             this.maskEl.mask(this.loadingText);
36593         }
36594         
36595         this.file = false;
36596         this.exif = {};
36597         
36598         if(typeof(file) === 'string'){
36599             this.loadCanvas(file);
36600             return;
36601         }
36602         
36603         if(!file || !this.urlAPI){
36604             return;
36605         }
36606         
36607         this.file = file;
36608         this.cropType = file.type;
36609         
36610         var _this = this;
36611         
36612         if(this.fireEvent('prepare', this, this.file) != false){
36613             
36614             var reader = new FileReader();
36615             
36616             reader.onload = function (e) {
36617                 if (e.target.error) {
36618                     Roo.log(e.target.error);
36619                     return;
36620                 }
36621                 
36622                 var buffer = e.target.result,
36623                     dataView = new DataView(buffer),
36624                     offset = 2,
36625                     maxOffset = dataView.byteLength - 4,
36626                     markerBytes,
36627                     markerLength;
36628                 
36629                 if (dataView.getUint16(0) === 0xffd8) {
36630                     while (offset < maxOffset) {
36631                         markerBytes = dataView.getUint16(offset);
36632                         
36633                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36634                             markerLength = dataView.getUint16(offset + 2) + 2;
36635                             if (offset + markerLength > dataView.byteLength) {
36636                                 Roo.log('Invalid meta data: Invalid segment size.');
36637                                 break;
36638                             }
36639                             
36640                             if(markerBytes == 0xffe1){
36641                                 _this.parseExifData(
36642                                     dataView,
36643                                     offset,
36644                                     markerLength
36645                                 );
36646                             }
36647                             
36648                             offset += markerLength;
36649                             
36650                             continue;
36651                         }
36652                         
36653                         break;
36654                     }
36655                     
36656                 }
36657                 
36658                 var url = _this.urlAPI.createObjectURL(_this.file);
36659                 
36660                 _this.loadCanvas(url);
36661                 
36662                 return;
36663             }
36664             
36665             reader.readAsArrayBuffer(this.file);
36666             
36667         }
36668         
36669     },
36670     
36671     parseExifData : function(dataView, offset, length)
36672     {
36673         var tiffOffset = offset + 10,
36674             littleEndian,
36675             dirOffset;
36676     
36677         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36678             // No Exif data, might be XMP data instead
36679             return;
36680         }
36681         
36682         // Check for the ASCII code for "Exif" (0x45786966):
36683         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36684             // No Exif data, might be XMP data instead
36685             return;
36686         }
36687         if (tiffOffset + 8 > dataView.byteLength) {
36688             Roo.log('Invalid Exif data: Invalid segment size.');
36689             return;
36690         }
36691         // Check for the two null bytes:
36692         if (dataView.getUint16(offset + 8) !== 0x0000) {
36693             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36694             return;
36695         }
36696         // Check the byte alignment:
36697         switch (dataView.getUint16(tiffOffset)) {
36698         case 0x4949:
36699             littleEndian = true;
36700             break;
36701         case 0x4D4D:
36702             littleEndian = false;
36703             break;
36704         default:
36705             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36706             return;
36707         }
36708         // Check for the TIFF tag marker (0x002A):
36709         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36710             Roo.log('Invalid Exif data: Missing TIFF marker.');
36711             return;
36712         }
36713         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36714         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36715         
36716         this.parseExifTags(
36717             dataView,
36718             tiffOffset,
36719             tiffOffset + dirOffset,
36720             littleEndian
36721         );
36722     },
36723     
36724     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36725     {
36726         var tagsNumber,
36727             dirEndOffset,
36728             i;
36729         if (dirOffset + 6 > dataView.byteLength) {
36730             Roo.log('Invalid Exif data: Invalid directory offset.');
36731             return;
36732         }
36733         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36734         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36735         if (dirEndOffset + 4 > dataView.byteLength) {
36736             Roo.log('Invalid Exif data: Invalid directory size.');
36737             return;
36738         }
36739         for (i = 0; i < tagsNumber; i += 1) {
36740             this.parseExifTag(
36741                 dataView,
36742                 tiffOffset,
36743                 dirOffset + 2 + 12 * i, // tag offset
36744                 littleEndian
36745             );
36746         }
36747         // Return the offset to the next directory:
36748         return dataView.getUint32(dirEndOffset, littleEndian);
36749     },
36750     
36751     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36752     {
36753         var tag = dataView.getUint16(offset, littleEndian);
36754         
36755         this.exif[tag] = this.getExifValue(
36756             dataView,
36757             tiffOffset,
36758             offset,
36759             dataView.getUint16(offset + 2, littleEndian), // tag type
36760             dataView.getUint32(offset + 4, littleEndian), // tag length
36761             littleEndian
36762         );
36763     },
36764     
36765     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36766     {
36767         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36768             tagSize,
36769             dataOffset,
36770             values,
36771             i,
36772             str,
36773             c;
36774     
36775         if (!tagType) {
36776             Roo.log('Invalid Exif data: Invalid tag type.');
36777             return;
36778         }
36779         
36780         tagSize = tagType.size * length;
36781         // Determine if the value is contained in the dataOffset bytes,
36782         // or if the value at the dataOffset is a pointer to the actual data:
36783         dataOffset = tagSize > 4 ?
36784                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36785         if (dataOffset + tagSize > dataView.byteLength) {
36786             Roo.log('Invalid Exif data: Invalid data offset.');
36787             return;
36788         }
36789         if (length === 1) {
36790             return tagType.getValue(dataView, dataOffset, littleEndian);
36791         }
36792         values = [];
36793         for (i = 0; i < length; i += 1) {
36794             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36795         }
36796         
36797         if (tagType.ascii) {
36798             str = '';
36799             // Concatenate the chars:
36800             for (i = 0; i < values.length; i += 1) {
36801                 c = values[i];
36802                 // Ignore the terminating NULL byte(s):
36803                 if (c === '\u0000') {
36804                     break;
36805                 }
36806                 str += c;
36807             }
36808             return str;
36809         }
36810         return values;
36811     }
36812     
36813 });
36814
36815 Roo.apply(Roo.bootstrap.UploadCropbox, {
36816     tags : {
36817         'Orientation': 0x0112
36818     },
36819     
36820     Orientation: {
36821             1: 0, //'top-left',
36822 //            2: 'top-right',
36823             3: 180, //'bottom-right',
36824 //            4: 'bottom-left',
36825 //            5: 'left-top',
36826             6: 90, //'right-top',
36827 //            7: 'right-bottom',
36828             8: 270 //'left-bottom'
36829     },
36830     
36831     exifTagTypes : {
36832         // byte, 8-bit unsigned int:
36833         1: {
36834             getValue: function (dataView, dataOffset) {
36835                 return dataView.getUint8(dataOffset);
36836             },
36837             size: 1
36838         },
36839         // ascii, 8-bit byte:
36840         2: {
36841             getValue: function (dataView, dataOffset) {
36842                 return String.fromCharCode(dataView.getUint8(dataOffset));
36843             },
36844             size: 1,
36845             ascii: true
36846         },
36847         // short, 16 bit int:
36848         3: {
36849             getValue: function (dataView, dataOffset, littleEndian) {
36850                 return dataView.getUint16(dataOffset, littleEndian);
36851             },
36852             size: 2
36853         },
36854         // long, 32 bit int:
36855         4: {
36856             getValue: function (dataView, dataOffset, littleEndian) {
36857                 return dataView.getUint32(dataOffset, littleEndian);
36858             },
36859             size: 4
36860         },
36861         // rational = two long values, first is numerator, second is denominator:
36862         5: {
36863             getValue: function (dataView, dataOffset, littleEndian) {
36864                 return dataView.getUint32(dataOffset, littleEndian) /
36865                     dataView.getUint32(dataOffset + 4, littleEndian);
36866             },
36867             size: 8
36868         },
36869         // slong, 32 bit signed int:
36870         9: {
36871             getValue: function (dataView, dataOffset, littleEndian) {
36872                 return dataView.getInt32(dataOffset, littleEndian);
36873             },
36874             size: 4
36875         },
36876         // srational, two slongs, first is numerator, second is denominator:
36877         10: {
36878             getValue: function (dataView, dataOffset, littleEndian) {
36879                 return dataView.getInt32(dataOffset, littleEndian) /
36880                     dataView.getInt32(dataOffset + 4, littleEndian);
36881             },
36882             size: 8
36883         }
36884     },
36885     
36886     footer : {
36887         STANDARD : [
36888             {
36889                 tag : 'div',
36890                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36891                 action : 'rotate-left',
36892                 cn : [
36893                     {
36894                         tag : 'button',
36895                         cls : 'btn btn-default',
36896                         html : '<i class="fa fa-undo"></i>'
36897                     }
36898                 ]
36899             },
36900             {
36901                 tag : 'div',
36902                 cls : 'btn-group roo-upload-cropbox-picture',
36903                 action : 'picture',
36904                 cn : [
36905                     {
36906                         tag : 'button',
36907                         cls : 'btn btn-default',
36908                         html : '<i class="fa fa-picture-o"></i>'
36909                     }
36910                 ]
36911             },
36912             {
36913                 tag : 'div',
36914                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36915                 action : 'rotate-right',
36916                 cn : [
36917                     {
36918                         tag : 'button',
36919                         cls : 'btn btn-default',
36920                         html : '<i class="fa fa-repeat"></i>'
36921                     }
36922                 ]
36923             }
36924         ],
36925         DOCUMENT : [
36926             {
36927                 tag : 'div',
36928                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36929                 action : 'rotate-left',
36930                 cn : [
36931                     {
36932                         tag : 'button',
36933                         cls : 'btn btn-default',
36934                         html : '<i class="fa fa-undo"></i>'
36935                     }
36936                 ]
36937             },
36938             {
36939                 tag : 'div',
36940                 cls : 'btn-group roo-upload-cropbox-download',
36941                 action : 'download',
36942                 cn : [
36943                     {
36944                         tag : 'button',
36945                         cls : 'btn btn-default',
36946                         html : '<i class="fa fa-download"></i>'
36947                     }
36948                 ]
36949             },
36950             {
36951                 tag : 'div',
36952                 cls : 'btn-group roo-upload-cropbox-crop',
36953                 action : 'crop',
36954                 cn : [
36955                     {
36956                         tag : 'button',
36957                         cls : 'btn btn-default',
36958                         html : '<i class="fa fa-crop"></i>'
36959                     }
36960                 ]
36961             },
36962             {
36963                 tag : 'div',
36964                 cls : 'btn-group roo-upload-cropbox-trash',
36965                 action : 'trash',
36966                 cn : [
36967                     {
36968                         tag : 'button',
36969                         cls : 'btn btn-default',
36970                         html : '<i class="fa fa-trash"></i>'
36971                     }
36972                 ]
36973             },
36974             {
36975                 tag : 'div',
36976                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36977                 action : 'rotate-right',
36978                 cn : [
36979                     {
36980                         tag : 'button',
36981                         cls : 'btn btn-default',
36982                         html : '<i class="fa fa-repeat"></i>'
36983                     }
36984                 ]
36985             }
36986         ],
36987         ROTATOR : [
36988             {
36989                 tag : 'div',
36990                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36991                 action : 'rotate-left',
36992                 cn : [
36993                     {
36994                         tag : 'button',
36995                         cls : 'btn btn-default',
36996                         html : '<i class="fa fa-undo"></i>'
36997                     }
36998                 ]
36999             },
37000             {
37001                 tag : 'div',
37002                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37003                 action : 'rotate-right',
37004                 cn : [
37005                     {
37006                         tag : 'button',
37007                         cls : 'btn btn-default',
37008                         html : '<i class="fa fa-repeat"></i>'
37009                     }
37010                 ]
37011             }
37012         ]
37013     }
37014 });
37015
37016 /*
37017 * Licence: LGPL
37018 */
37019
37020 /**
37021  * @class Roo.bootstrap.DocumentManager
37022  * @extends Roo.bootstrap.Component
37023  * Bootstrap DocumentManager class
37024  * @cfg {String} paramName default 'imageUpload'
37025  * @cfg {String} toolTipName default 'filename'
37026  * @cfg {String} method default POST
37027  * @cfg {String} url action url
37028  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37029  * @cfg {Boolean} multiple multiple upload default true
37030  * @cfg {Number} thumbSize default 300
37031  * @cfg {String} fieldLabel
37032  * @cfg {Number} labelWidth default 4
37033  * @cfg {String} labelAlign (left|top) default left
37034  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37035 * @cfg {Number} labellg set the width of label (1-12)
37036  * @cfg {Number} labelmd set the width of label (1-12)
37037  * @cfg {Number} labelsm set the width of label (1-12)
37038  * @cfg {Number} labelxs set the width of label (1-12)
37039  * 
37040  * @constructor
37041  * Create a new DocumentManager
37042  * @param {Object} config The config object
37043  */
37044
37045 Roo.bootstrap.DocumentManager = function(config){
37046     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37047     
37048     this.files = [];
37049     this.delegates = [];
37050     
37051     this.addEvents({
37052         /**
37053          * @event initial
37054          * Fire when initial the DocumentManager
37055          * @param {Roo.bootstrap.DocumentManager} this
37056          */
37057         "initial" : true,
37058         /**
37059          * @event inspect
37060          * inspect selected file
37061          * @param {Roo.bootstrap.DocumentManager} this
37062          * @param {File} file
37063          */
37064         "inspect" : true,
37065         /**
37066          * @event exception
37067          * Fire when xhr load exception
37068          * @param {Roo.bootstrap.DocumentManager} this
37069          * @param {XMLHttpRequest} xhr
37070          */
37071         "exception" : true,
37072         /**
37073          * @event afterupload
37074          * Fire when xhr load exception
37075          * @param {Roo.bootstrap.DocumentManager} this
37076          * @param {XMLHttpRequest} xhr
37077          */
37078         "afterupload" : true,
37079         /**
37080          * @event prepare
37081          * prepare the form data
37082          * @param {Roo.bootstrap.DocumentManager} this
37083          * @param {Object} formData
37084          */
37085         "prepare" : true,
37086         /**
37087          * @event remove
37088          * Fire when remove the file
37089          * @param {Roo.bootstrap.DocumentManager} this
37090          * @param {Object} file
37091          */
37092         "remove" : true,
37093         /**
37094          * @event refresh
37095          * Fire after refresh the file
37096          * @param {Roo.bootstrap.DocumentManager} this
37097          */
37098         "refresh" : true,
37099         /**
37100          * @event click
37101          * Fire after click the image
37102          * @param {Roo.bootstrap.DocumentManager} this
37103          * @param {Object} file
37104          */
37105         "click" : true,
37106         /**
37107          * @event edit
37108          * Fire when upload a image and editable set to true
37109          * @param {Roo.bootstrap.DocumentManager} this
37110          * @param {Object} file
37111          */
37112         "edit" : true,
37113         /**
37114          * @event beforeselectfile
37115          * Fire before select file
37116          * @param {Roo.bootstrap.DocumentManager} this
37117          */
37118         "beforeselectfile" : true,
37119         /**
37120          * @event process
37121          * Fire before process file
37122          * @param {Roo.bootstrap.DocumentManager} this
37123          * @param {Object} file
37124          */
37125         "process" : true,
37126         /**
37127          * @event previewrendered
37128          * Fire when preview rendered
37129          * @param {Roo.bootstrap.DocumentManager} this
37130          * @param {Object} file
37131          */
37132         "previewrendered" : true,
37133         /**
37134          */
37135         "previewResize" : true
37136         
37137     });
37138 };
37139
37140 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37141     
37142     boxes : 0,
37143     inputName : '',
37144     thumbSize : 300,
37145     multiple : true,
37146     files : false,
37147     method : 'POST',
37148     url : '',
37149     paramName : 'imageUpload',
37150     toolTipName : 'filename',
37151     fieldLabel : '',
37152     labelWidth : 4,
37153     labelAlign : 'left',
37154     editable : true,
37155     delegates : false,
37156     xhr : false, 
37157     
37158     labellg : 0,
37159     labelmd : 0,
37160     labelsm : 0,
37161     labelxs : 0,
37162     
37163     getAutoCreate : function()
37164     {   
37165         var managerWidget = {
37166             tag : 'div',
37167             cls : 'roo-document-manager',
37168             cn : [
37169                 {
37170                     tag : 'input',
37171                     cls : 'roo-document-manager-selector',
37172                     type : 'file'
37173                 },
37174                 {
37175                     tag : 'div',
37176                     cls : 'roo-document-manager-uploader',
37177                     cn : [
37178                         {
37179                             tag : 'div',
37180                             cls : 'roo-document-manager-upload-btn',
37181                             html : '<i class="fa fa-plus"></i>'
37182                         }
37183                     ]
37184                     
37185                 }
37186             ]
37187         };
37188         
37189         var content = [
37190             {
37191                 tag : 'div',
37192                 cls : 'column col-md-12',
37193                 cn : managerWidget
37194             }
37195         ];
37196         
37197         if(this.fieldLabel.length){
37198             
37199             content = [
37200                 {
37201                     tag : 'div',
37202                     cls : 'column col-md-12',
37203                     html : this.fieldLabel
37204                 },
37205                 {
37206                     tag : 'div',
37207                     cls : 'column col-md-12',
37208                     cn : managerWidget
37209                 }
37210             ];
37211
37212             if(this.labelAlign == 'left'){
37213                 content = [
37214                     {
37215                         tag : 'div',
37216                         cls : 'column',
37217                         html : this.fieldLabel
37218                     },
37219                     {
37220                         tag : 'div',
37221                         cls : 'column',
37222                         cn : managerWidget
37223                     }
37224                 ];
37225                 
37226                 if(this.labelWidth > 12){
37227                     content[0].style = "width: " + this.labelWidth + 'px';
37228                 }
37229
37230                 if(this.labelWidth < 13 && this.labelmd == 0){
37231                     this.labelmd = this.labelWidth;
37232                 }
37233
37234                 if(this.labellg > 0){
37235                     content[0].cls += ' col-lg-' + this.labellg;
37236                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37237                 }
37238
37239                 if(this.labelmd > 0){
37240                     content[0].cls += ' col-md-' + this.labelmd;
37241                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37242                 }
37243
37244                 if(this.labelsm > 0){
37245                     content[0].cls += ' col-sm-' + this.labelsm;
37246                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37247                 }
37248
37249                 if(this.labelxs > 0){
37250                     content[0].cls += ' col-xs-' + this.labelxs;
37251                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37252                 }
37253                 
37254             }
37255         }
37256         
37257         var cfg = {
37258             tag : 'div',
37259             cls : 'row clearfix',
37260             cn : content
37261         };
37262         
37263         return cfg;
37264         
37265     },
37266     
37267     initEvents : function()
37268     {
37269         this.managerEl = this.el.select('.roo-document-manager', true).first();
37270         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37271         
37272         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37273         this.selectorEl.hide();
37274         
37275         if(this.multiple){
37276             this.selectorEl.attr('multiple', 'multiple');
37277         }
37278         
37279         this.selectorEl.on('change', this.onFileSelected, this);
37280         
37281         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37282         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37283         
37284         this.uploader.on('click', this.onUploaderClick, this);
37285         
37286         this.renderProgressDialog();
37287         
37288         var _this = this;
37289         
37290         window.addEventListener("resize", function() { _this.refresh(); } );
37291         
37292         this.fireEvent('initial', this);
37293     },
37294     
37295     renderProgressDialog : function()
37296     {
37297         var _this = this;
37298         
37299         this.progressDialog = new Roo.bootstrap.Modal({
37300             cls : 'roo-document-manager-progress-dialog',
37301             allow_close : false,
37302             animate : false,
37303             title : '',
37304             buttons : [
37305                 {
37306                     name  :'cancel',
37307                     weight : 'danger',
37308                     html : 'Cancel'
37309                 }
37310             ], 
37311             listeners : { 
37312                 btnclick : function() {
37313                     _this.uploadCancel();
37314                     this.hide();
37315                 }
37316             }
37317         });
37318          
37319         this.progressDialog.render(Roo.get(document.body));
37320          
37321         this.progress = new Roo.bootstrap.Progress({
37322             cls : 'roo-document-manager-progress',
37323             active : true,
37324             striped : true
37325         });
37326         
37327         this.progress.render(this.progressDialog.getChildContainer());
37328         
37329         this.progressBar = new Roo.bootstrap.ProgressBar({
37330             cls : 'roo-document-manager-progress-bar',
37331             aria_valuenow : 0,
37332             aria_valuemin : 0,
37333             aria_valuemax : 12,
37334             panel : 'success'
37335         });
37336         
37337         this.progressBar.render(this.progress.getChildContainer());
37338     },
37339     
37340     onUploaderClick : function(e)
37341     {
37342         e.preventDefault();
37343      
37344         if(this.fireEvent('beforeselectfile', this) != false){
37345             this.selectorEl.dom.click();
37346         }
37347         
37348     },
37349     
37350     onFileSelected : function(e)
37351     {
37352         e.preventDefault();
37353         
37354         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37355             return;
37356         }
37357         
37358         Roo.each(this.selectorEl.dom.files, function(file){
37359             if(this.fireEvent('inspect', this, file) != false){
37360                 this.files.push(file);
37361             }
37362         }, this);
37363         
37364         this.queue();
37365         
37366     },
37367     
37368     queue : function()
37369     {
37370         this.selectorEl.dom.value = '';
37371         
37372         if(!this.files || !this.files.length){
37373             return;
37374         }
37375         
37376         if(this.boxes > 0 && this.files.length > this.boxes){
37377             this.files = this.files.slice(0, this.boxes);
37378         }
37379         
37380         this.uploader.show();
37381         
37382         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37383             this.uploader.hide();
37384         }
37385         
37386         var _this = this;
37387         
37388         var files = [];
37389         
37390         var docs = [];
37391         
37392         Roo.each(this.files, function(file){
37393             
37394             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37395                 var f = this.renderPreview(file);
37396                 files.push(f);
37397                 return;
37398             }
37399             
37400             if(file.type.indexOf('image') != -1){
37401                 this.delegates.push(
37402                     (function(){
37403                         _this.process(file);
37404                     }).createDelegate(this)
37405                 );
37406         
37407                 return;
37408             }
37409             
37410             docs.push(
37411                 (function(){
37412                     _this.process(file);
37413                 }).createDelegate(this)
37414             );
37415             
37416         }, this);
37417         
37418         this.files = files;
37419         
37420         this.delegates = this.delegates.concat(docs);
37421         
37422         if(!this.delegates.length){
37423             this.refresh();
37424             return;
37425         }
37426         
37427         this.progressBar.aria_valuemax = this.delegates.length;
37428         
37429         this.arrange();
37430         
37431         return;
37432     },
37433     
37434     arrange : function()
37435     {
37436         if(!this.delegates.length){
37437             this.progressDialog.hide();
37438             this.refresh();
37439             return;
37440         }
37441         
37442         var delegate = this.delegates.shift();
37443         
37444         this.progressDialog.show();
37445         
37446         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37447         
37448         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37449         
37450         delegate();
37451     },
37452     
37453     refresh : function()
37454     {
37455         this.uploader.show();
37456         
37457         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37458             this.uploader.hide();
37459         }
37460         
37461         Roo.isTouch ? this.closable(false) : this.closable(true);
37462         
37463         this.fireEvent('refresh', this);
37464     },
37465     
37466     onRemove : function(e, el, o)
37467     {
37468         e.preventDefault();
37469         
37470         this.fireEvent('remove', this, o);
37471         
37472     },
37473     
37474     remove : function(o)
37475     {
37476         var files = [];
37477         
37478         Roo.each(this.files, function(file){
37479             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37480                 files.push(file);
37481                 return;
37482             }
37483
37484             o.target.remove();
37485
37486         }, this);
37487         
37488         this.files = files;
37489         
37490         this.refresh();
37491     },
37492     
37493     clear : function()
37494     {
37495         Roo.each(this.files, function(file){
37496             if(!file.target){
37497                 return;
37498             }
37499             
37500             file.target.remove();
37501
37502         }, this);
37503         
37504         this.files = [];
37505         
37506         this.refresh();
37507     },
37508     
37509     onClick : function(e, el, o)
37510     {
37511         e.preventDefault();
37512         
37513         this.fireEvent('click', this, o);
37514         
37515     },
37516     
37517     closable : function(closable)
37518     {
37519         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37520             
37521             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37522             
37523             if(closable){
37524                 el.show();
37525                 return;
37526             }
37527             
37528             el.hide();
37529             
37530         }, this);
37531     },
37532     
37533     xhrOnLoad : function(xhr)
37534     {
37535         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37536             el.remove();
37537         }, this);
37538         
37539         if (xhr.readyState !== 4) {
37540             this.arrange();
37541             this.fireEvent('exception', this, xhr);
37542             return;
37543         }
37544
37545         var response = Roo.decode(xhr.responseText);
37546         
37547         if(!response.success){
37548             this.arrange();
37549             this.fireEvent('exception', this, xhr);
37550             return;
37551         }
37552         
37553         var file = this.renderPreview(response.data);
37554         
37555         this.files.push(file);
37556         
37557         this.arrange();
37558         
37559         this.fireEvent('afterupload', this, xhr);
37560         
37561     },
37562     
37563     xhrOnError : function(xhr)
37564     {
37565         Roo.log('xhr on error');
37566         
37567         var response = Roo.decode(xhr.responseText);
37568           
37569         Roo.log(response);
37570         
37571         this.arrange();
37572     },
37573     
37574     process : function(file)
37575     {
37576         if(this.fireEvent('process', this, file) !== false){
37577             if(this.editable && file.type.indexOf('image') != -1){
37578                 this.fireEvent('edit', this, file);
37579                 return;
37580             }
37581
37582             this.uploadStart(file, false);
37583
37584             return;
37585         }
37586         
37587     },
37588     
37589     uploadStart : function(file, crop)
37590     {
37591         this.xhr = new XMLHttpRequest();
37592         
37593         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37594             this.arrange();
37595             return;
37596         }
37597         
37598         file.xhr = this.xhr;
37599             
37600         this.managerEl.createChild({
37601             tag : 'div',
37602             cls : 'roo-document-manager-loading',
37603             cn : [
37604                 {
37605                     tag : 'div',
37606                     tooltip : file.name,
37607                     cls : 'roo-document-manager-thumb',
37608                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37609                 }
37610             ]
37611
37612         });
37613
37614         this.xhr.open(this.method, this.url, true);
37615         
37616         var headers = {
37617             "Accept": "application/json",
37618             "Cache-Control": "no-cache",
37619             "X-Requested-With": "XMLHttpRequest"
37620         };
37621         
37622         for (var headerName in headers) {
37623             var headerValue = headers[headerName];
37624             if (headerValue) {
37625                 this.xhr.setRequestHeader(headerName, headerValue);
37626             }
37627         }
37628         
37629         var _this = this;
37630         
37631         this.xhr.onload = function()
37632         {
37633             _this.xhrOnLoad(_this.xhr);
37634         }
37635         
37636         this.xhr.onerror = function()
37637         {
37638             _this.xhrOnError(_this.xhr);
37639         }
37640         
37641         var formData = new FormData();
37642
37643         formData.append('returnHTML', 'NO');
37644         
37645         if(crop){
37646             formData.append('crop', crop);
37647         }
37648         
37649         formData.append(this.paramName, file, file.name);
37650         
37651         var options = {
37652             file : file, 
37653             manually : false
37654         };
37655         
37656         if(this.fireEvent('prepare', this, formData, options) != false){
37657             
37658             if(options.manually){
37659                 return;
37660             }
37661             
37662             this.xhr.send(formData);
37663             return;
37664         };
37665         
37666         this.uploadCancel();
37667     },
37668     
37669     uploadCancel : function()
37670     {
37671         if (this.xhr) {
37672             this.xhr.abort();
37673         }
37674         
37675         this.delegates = [];
37676         
37677         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37678             el.remove();
37679         }, this);
37680         
37681         this.arrange();
37682     },
37683     
37684     renderPreview : function(file)
37685     {
37686         if(typeof(file.target) != 'undefined' && file.target){
37687             return file;
37688         }
37689         
37690         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37691         
37692         var previewEl = this.managerEl.createChild({
37693             tag : 'div',
37694             cls : 'roo-document-manager-preview',
37695             cn : [
37696                 {
37697                     tag : 'div',
37698                     tooltip : file[this.toolTipName],
37699                     cls : 'roo-document-manager-thumb',
37700                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37701                 },
37702                 {
37703                     tag : 'button',
37704                     cls : 'close',
37705                     html : '<i class="fa fa-times-circle"></i>'
37706                 }
37707             ]
37708         });
37709
37710         var close = previewEl.select('button.close', true).first();
37711
37712         close.on('click', this.onRemove, this, file);
37713
37714         file.target = previewEl;
37715
37716         var image = previewEl.select('img', true).first();
37717         
37718         var _this = this;
37719         
37720         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37721         
37722         image.on('click', this.onClick, this, file);
37723         
37724         this.fireEvent('previewrendered', this, file);
37725         
37726         return file;
37727         
37728     },
37729     
37730     onPreviewLoad : function(file, image)
37731     {
37732         if(typeof(file.target) == 'undefined' || !file.target){
37733             return;
37734         }
37735         
37736         var width = image.dom.naturalWidth || image.dom.width;
37737         var height = image.dom.naturalHeight || image.dom.height;
37738         
37739         if(!this.previewResize) {
37740             return;
37741         }
37742         
37743         if(width > height){
37744             file.target.addClass('wide');
37745             return;
37746         }
37747         
37748         file.target.addClass('tall');
37749         return;
37750         
37751     },
37752     
37753     uploadFromSource : function(file, crop)
37754     {
37755         this.xhr = new XMLHttpRequest();
37756         
37757         this.managerEl.createChild({
37758             tag : 'div',
37759             cls : 'roo-document-manager-loading',
37760             cn : [
37761                 {
37762                     tag : 'div',
37763                     tooltip : file.name,
37764                     cls : 'roo-document-manager-thumb',
37765                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37766                 }
37767             ]
37768
37769         });
37770
37771         this.xhr.open(this.method, this.url, true);
37772         
37773         var headers = {
37774             "Accept": "application/json",
37775             "Cache-Control": "no-cache",
37776             "X-Requested-With": "XMLHttpRequest"
37777         };
37778         
37779         for (var headerName in headers) {
37780             var headerValue = headers[headerName];
37781             if (headerValue) {
37782                 this.xhr.setRequestHeader(headerName, headerValue);
37783             }
37784         }
37785         
37786         var _this = this;
37787         
37788         this.xhr.onload = function()
37789         {
37790             _this.xhrOnLoad(_this.xhr);
37791         }
37792         
37793         this.xhr.onerror = function()
37794         {
37795             _this.xhrOnError(_this.xhr);
37796         }
37797         
37798         var formData = new FormData();
37799
37800         formData.append('returnHTML', 'NO');
37801         
37802         formData.append('crop', crop);
37803         
37804         if(typeof(file.filename) != 'undefined'){
37805             formData.append('filename', file.filename);
37806         }
37807         
37808         if(typeof(file.mimetype) != 'undefined'){
37809             formData.append('mimetype', file.mimetype);
37810         }
37811         
37812         Roo.log(formData);
37813         
37814         if(this.fireEvent('prepare', this, formData) != false){
37815             this.xhr.send(formData);
37816         };
37817     }
37818 });
37819
37820 /*
37821 * Licence: LGPL
37822 */
37823
37824 /**
37825  * @class Roo.bootstrap.DocumentViewer
37826  * @extends Roo.bootstrap.Component
37827  * Bootstrap DocumentViewer class
37828  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37829  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37830  * 
37831  * @constructor
37832  * Create a new DocumentViewer
37833  * @param {Object} config The config object
37834  */
37835
37836 Roo.bootstrap.DocumentViewer = function(config){
37837     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37838     
37839     this.addEvents({
37840         /**
37841          * @event initial
37842          * Fire after initEvent
37843          * @param {Roo.bootstrap.DocumentViewer} this
37844          */
37845         "initial" : true,
37846         /**
37847          * @event click
37848          * Fire after click
37849          * @param {Roo.bootstrap.DocumentViewer} this
37850          */
37851         "click" : true,
37852         /**
37853          * @event download
37854          * Fire after download button
37855          * @param {Roo.bootstrap.DocumentViewer} this
37856          */
37857         "download" : true,
37858         /**
37859          * @event trash
37860          * Fire after trash button
37861          * @param {Roo.bootstrap.DocumentViewer} this
37862          */
37863         "trash" : true
37864         
37865     });
37866 };
37867
37868 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37869     
37870     showDownload : true,
37871     
37872     showTrash : true,
37873     
37874     getAutoCreate : function()
37875     {
37876         var cfg = {
37877             tag : 'div',
37878             cls : 'roo-document-viewer',
37879             cn : [
37880                 {
37881                     tag : 'div',
37882                     cls : 'roo-document-viewer-body',
37883                     cn : [
37884                         {
37885                             tag : 'div',
37886                             cls : 'roo-document-viewer-thumb',
37887                             cn : [
37888                                 {
37889                                     tag : 'img',
37890                                     cls : 'roo-document-viewer-image'
37891                                 }
37892                             ]
37893                         }
37894                     ]
37895                 },
37896                 {
37897                     tag : 'div',
37898                     cls : 'roo-document-viewer-footer',
37899                     cn : {
37900                         tag : 'div',
37901                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37902                         cn : [
37903                             {
37904                                 tag : 'div',
37905                                 cls : 'btn-group roo-document-viewer-download',
37906                                 cn : [
37907                                     {
37908                                         tag : 'button',
37909                                         cls : 'btn btn-default',
37910                                         html : '<i class="fa fa-download"></i>'
37911                                     }
37912                                 ]
37913                             },
37914                             {
37915                                 tag : 'div',
37916                                 cls : 'btn-group roo-document-viewer-trash',
37917                                 cn : [
37918                                     {
37919                                         tag : 'button',
37920                                         cls : 'btn btn-default',
37921                                         html : '<i class="fa fa-trash"></i>'
37922                                     }
37923                                 ]
37924                             }
37925                         ]
37926                     }
37927                 }
37928             ]
37929         };
37930         
37931         return cfg;
37932     },
37933     
37934     initEvents : function()
37935     {
37936         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37937         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37938         
37939         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37940         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37941         
37942         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37943         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37944         
37945         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37946         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37947         
37948         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37949         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37950         
37951         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37952         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37953         
37954         this.bodyEl.on('click', this.onClick, this);
37955         this.downloadBtn.on('click', this.onDownload, this);
37956         this.trashBtn.on('click', this.onTrash, this);
37957         
37958         this.downloadBtn.hide();
37959         this.trashBtn.hide();
37960         
37961         if(this.showDownload){
37962             this.downloadBtn.show();
37963         }
37964         
37965         if(this.showTrash){
37966             this.trashBtn.show();
37967         }
37968         
37969         if(!this.showDownload && !this.showTrash) {
37970             this.footerEl.hide();
37971         }
37972         
37973     },
37974     
37975     initial : function()
37976     {
37977         this.fireEvent('initial', this);
37978         
37979     },
37980     
37981     onClick : function(e)
37982     {
37983         e.preventDefault();
37984         
37985         this.fireEvent('click', this);
37986     },
37987     
37988     onDownload : function(e)
37989     {
37990         e.preventDefault();
37991         
37992         this.fireEvent('download', this);
37993     },
37994     
37995     onTrash : function(e)
37996     {
37997         e.preventDefault();
37998         
37999         this.fireEvent('trash', this);
38000     }
38001     
38002 });
38003 /*
38004  * - LGPL
38005  *
38006  * FieldLabel
38007  * 
38008  */
38009
38010 /**
38011  * @class Roo.bootstrap.form.FieldLabel
38012  * @extends Roo.bootstrap.Component
38013  * Bootstrap FieldLabel class
38014  * @cfg {String} html contents of the element
38015  * @cfg {String} tag tag of the element default label
38016  * @cfg {String} cls class of the element
38017  * @cfg {String} target label target 
38018  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38019  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38020  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38021  * @cfg {String} iconTooltip default "This field is required"
38022  * @cfg {String} indicatorpos (left|right) default left
38023  * 
38024  * @constructor
38025  * Create a new FieldLabel
38026  * @param {Object} config The config object
38027  */
38028
38029 Roo.bootstrap.form.FieldLabel = function(config){
38030     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38031     
38032     this.addEvents({
38033             /**
38034              * @event invalid
38035              * Fires after the field has been marked as invalid.
38036              * @param {Roo.form.FieldLabel} this
38037              * @param {String} msg The validation message
38038              */
38039             invalid : true,
38040             /**
38041              * @event valid
38042              * Fires after the field has been validated with no errors.
38043              * @param {Roo.form.FieldLabel} this
38044              */
38045             valid : true
38046         });
38047 };
38048
38049 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38050     
38051     tag: 'label',
38052     cls: '',
38053     html: '',
38054     target: '',
38055     allowBlank : true,
38056     invalidClass : 'has-warning',
38057     validClass : 'has-success',
38058     iconTooltip : 'This field is required',
38059     indicatorpos : 'left',
38060     
38061     getAutoCreate : function(){
38062         
38063         var cls = "";
38064         if (!this.allowBlank) {
38065             cls  = "visible";
38066         }
38067         
38068         var cfg = {
38069             tag : this.tag,
38070             cls : 'roo-bootstrap-field-label ' + this.cls,
38071             for : this.target,
38072             cn : [
38073                 {
38074                     tag : 'i',
38075                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38076                     tooltip : this.iconTooltip
38077                 },
38078                 {
38079                     tag : 'span',
38080                     html : this.html
38081                 }
38082             ] 
38083         };
38084         
38085         if(this.indicatorpos == 'right'){
38086             var cfg = {
38087                 tag : this.tag,
38088                 cls : 'roo-bootstrap-field-label ' + this.cls,
38089                 for : this.target,
38090                 cn : [
38091                     {
38092                         tag : 'span',
38093                         html : this.html
38094                     },
38095                     {
38096                         tag : 'i',
38097                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38098                         tooltip : this.iconTooltip
38099                     }
38100                 ] 
38101             };
38102         }
38103         
38104         return cfg;
38105     },
38106     
38107     initEvents: function() 
38108     {
38109         Roo.bootstrap.Element.superclass.initEvents.call(this);
38110         
38111         this.indicator = this.indicatorEl();
38112         
38113         if(this.indicator){
38114             this.indicator.removeClass('visible');
38115             this.indicator.addClass('invisible');
38116         }
38117         
38118         Roo.bootstrap.form.FieldLabel.register(this);
38119     },
38120     
38121     indicatorEl : function()
38122     {
38123         var indicator = this.el.select('i.roo-required-indicator',true).first();
38124         
38125         if(!indicator){
38126             return false;
38127         }
38128         
38129         return indicator;
38130         
38131     },
38132     
38133     /**
38134      * Mark this field as valid
38135      */
38136     markValid : function()
38137     {
38138         if(this.indicator){
38139             this.indicator.removeClass('visible');
38140             this.indicator.addClass('invisible');
38141         }
38142         if (Roo.bootstrap.version == 3) {
38143             this.el.removeClass(this.invalidClass);
38144             this.el.addClass(this.validClass);
38145         } else {
38146             this.el.removeClass('is-invalid');
38147             this.el.addClass('is-valid');
38148         }
38149         
38150         
38151         this.fireEvent('valid', this);
38152     },
38153     
38154     /**
38155      * Mark this field as invalid
38156      * @param {String} msg The validation message
38157      */
38158     markInvalid : function(msg)
38159     {
38160         if(this.indicator){
38161             this.indicator.removeClass('invisible');
38162             this.indicator.addClass('visible');
38163         }
38164           if (Roo.bootstrap.version == 3) {
38165             this.el.removeClass(this.validClass);
38166             this.el.addClass(this.invalidClass);
38167         } else {
38168             this.el.removeClass('is-valid');
38169             this.el.addClass('is-invalid');
38170         }
38171         
38172         
38173         this.fireEvent('invalid', this, msg);
38174     }
38175     
38176    
38177 });
38178
38179 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38180     
38181     groups: {},
38182     
38183      /**
38184     * register a FieldLabel Group
38185     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38186     */
38187     register : function(label)
38188     {
38189         if(this.groups.hasOwnProperty(label.target)){
38190             return;
38191         }
38192      
38193         this.groups[label.target] = label;
38194         
38195     },
38196     /**
38197     * fetch a FieldLabel Group based on the target
38198     * @param {string} target
38199     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38200     */
38201     get: function(target) {
38202         if (typeof(this.groups[target]) == 'undefined') {
38203             return false;
38204         }
38205         
38206         return this.groups[target] ;
38207     }
38208 });
38209
38210  
38211
38212  /*
38213  * - LGPL
38214  *
38215  * page DateSplitField.
38216  * 
38217  */
38218
38219
38220 /**
38221  * @class Roo.bootstrap.form.DateSplitField
38222  * @extends Roo.bootstrap.Component
38223  * Bootstrap DateSplitField class
38224  * @cfg {string} fieldLabel - the label associated
38225  * @cfg {Number} labelWidth set the width of label (0-12)
38226  * @cfg {String} labelAlign (top|left)
38227  * @cfg {Boolean} dayAllowBlank (true|false) default false
38228  * @cfg {Boolean} monthAllowBlank (true|false) default false
38229  * @cfg {Boolean} yearAllowBlank (true|false) default false
38230  * @cfg {string} dayPlaceholder 
38231  * @cfg {string} monthPlaceholder
38232  * @cfg {string} yearPlaceholder
38233  * @cfg {string} dayFormat default 'd'
38234  * @cfg {string} monthFormat default 'm'
38235  * @cfg {string} yearFormat default 'Y'
38236  * @cfg {Number} labellg set the width of label (1-12)
38237  * @cfg {Number} labelmd set the width of label (1-12)
38238  * @cfg {Number} labelsm set the width of label (1-12)
38239  * @cfg {Number} labelxs set the width of label (1-12)
38240
38241  *     
38242  * @constructor
38243  * Create a new DateSplitField
38244  * @param {Object} config The config object
38245  */
38246
38247 Roo.bootstrap.form.DateSplitField = function(config){
38248     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38249     
38250     this.addEvents({
38251         // raw events
38252          /**
38253          * @event years
38254          * getting the data of years
38255          * @param {Roo.bootstrap.form.DateSplitField} this
38256          * @param {Object} years
38257          */
38258         "years" : true,
38259         /**
38260          * @event days
38261          * getting the data of days
38262          * @param {Roo.bootstrap.form.DateSplitField} this
38263          * @param {Object} days
38264          */
38265         "days" : true,
38266         /**
38267          * @event invalid
38268          * Fires after the field has been marked as invalid.
38269          * @param {Roo.form.Field} this
38270          * @param {String} msg The validation message
38271          */
38272         invalid : true,
38273        /**
38274          * @event valid
38275          * Fires after the field has been validated with no errors.
38276          * @param {Roo.form.Field} this
38277          */
38278         valid : true
38279     });
38280 };
38281
38282 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38283     
38284     fieldLabel : '',
38285     labelAlign : 'top',
38286     labelWidth : 3,
38287     dayAllowBlank : false,
38288     monthAllowBlank : false,
38289     yearAllowBlank : false,
38290     dayPlaceholder : '',
38291     monthPlaceholder : '',
38292     yearPlaceholder : '',
38293     dayFormat : 'd',
38294     monthFormat : 'm',
38295     yearFormat : 'Y',
38296     isFormField : true,
38297     labellg : 0,
38298     labelmd : 0,
38299     labelsm : 0,
38300     labelxs : 0,
38301     
38302     getAutoCreate : function()
38303     {
38304         var cfg = {
38305             tag : 'div',
38306             cls : 'row roo-date-split-field-group',
38307             cn : [
38308                 {
38309                     tag : 'input',
38310                     type : 'hidden',
38311                     cls : 'form-hidden-field roo-date-split-field-group-value',
38312                     name : this.name
38313                 }
38314             ]
38315         };
38316         
38317         var labelCls = 'col-md-12';
38318         var contentCls = 'col-md-4';
38319         
38320         if(this.fieldLabel){
38321             
38322             var label = {
38323                 tag : 'div',
38324                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38325                 cn : [
38326                     {
38327                         tag : 'label',
38328                         html : this.fieldLabel
38329                     }
38330                 ]
38331             };
38332             
38333             if(this.labelAlign == 'left'){
38334             
38335                 if(this.labelWidth > 12){
38336                     label.style = "width: " + this.labelWidth + 'px';
38337                 }
38338
38339                 if(this.labelWidth < 13 && this.labelmd == 0){
38340                     this.labelmd = this.labelWidth;
38341                 }
38342
38343                 if(this.labellg > 0){
38344                     labelCls = ' col-lg-' + this.labellg;
38345                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38346                 }
38347
38348                 if(this.labelmd > 0){
38349                     labelCls = ' col-md-' + this.labelmd;
38350                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38351                 }
38352
38353                 if(this.labelsm > 0){
38354                     labelCls = ' col-sm-' + this.labelsm;
38355                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38356                 }
38357
38358                 if(this.labelxs > 0){
38359                     labelCls = ' col-xs-' + this.labelxs;
38360                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38361                 }
38362             }
38363             
38364             label.cls += ' ' + labelCls;
38365             
38366             cfg.cn.push(label);
38367         }
38368         
38369         Roo.each(['day', 'month', 'year'], function(t){
38370             cfg.cn.push({
38371                 tag : 'div',
38372                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38373             });
38374         }, this);
38375         
38376         return cfg;
38377     },
38378     
38379     inputEl: function ()
38380     {
38381         return this.el.select('.roo-date-split-field-group-value', true).first();
38382     },
38383     
38384     onRender : function(ct, position) 
38385     {
38386         var _this = this;
38387         
38388         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38389         
38390         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38391         
38392         this.dayField = new Roo.bootstrap.form.ComboBox({
38393             allowBlank : this.dayAllowBlank,
38394             alwaysQuery : true,
38395             displayField : 'value',
38396             editable : false,
38397             fieldLabel : '',
38398             forceSelection : true,
38399             mode : 'local',
38400             placeholder : this.dayPlaceholder,
38401             selectOnFocus : true,
38402             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38403             triggerAction : 'all',
38404             typeAhead : true,
38405             valueField : 'value',
38406             store : new Roo.data.SimpleStore({
38407                 data : (function() {    
38408                     var days = [];
38409                     _this.fireEvent('days', _this, days);
38410                     return days;
38411                 })(),
38412                 fields : [ 'value' ]
38413             }),
38414             listeners : {
38415                 select : function (_self, record, index)
38416                 {
38417                     _this.setValue(_this.getValue());
38418                 }
38419             }
38420         });
38421
38422         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38423         
38424         this.monthField = new Roo.bootstrap.form.MonthField({
38425             after : '<i class=\"fa fa-calendar\"></i>',
38426             allowBlank : this.monthAllowBlank,
38427             placeholder : this.monthPlaceholder,
38428             readOnly : true,
38429             listeners : {
38430                 render : function (_self)
38431                 {
38432                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38433                         e.preventDefault();
38434                         _self.focus();
38435                     });
38436                 },
38437                 select : function (_self, oldvalue, newvalue)
38438                 {
38439                     _this.setValue(_this.getValue());
38440                 }
38441             }
38442         });
38443         
38444         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38445         
38446         this.yearField = new Roo.bootstrap.form.ComboBox({
38447             allowBlank : this.yearAllowBlank,
38448             alwaysQuery : true,
38449             displayField : 'value',
38450             editable : false,
38451             fieldLabel : '',
38452             forceSelection : true,
38453             mode : 'local',
38454             placeholder : this.yearPlaceholder,
38455             selectOnFocus : true,
38456             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38457             triggerAction : 'all',
38458             typeAhead : true,
38459             valueField : 'value',
38460             store : new Roo.data.SimpleStore({
38461                 data : (function() {
38462                     var years = [];
38463                     _this.fireEvent('years', _this, years);
38464                     return years;
38465                 })(),
38466                 fields : [ 'value' ]
38467             }),
38468             listeners : {
38469                 select : function (_self, record, index)
38470                 {
38471                     _this.setValue(_this.getValue());
38472                 }
38473             }
38474         });
38475
38476         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38477     },
38478     
38479     setValue : function(v, format)
38480     {
38481         this.inputEl.dom.value = v;
38482         
38483         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38484         
38485         var d = Date.parseDate(v, f);
38486         
38487         if(!d){
38488             this.validate();
38489             return;
38490         }
38491         
38492         this.setDay(d.format(this.dayFormat));
38493         this.setMonth(d.format(this.monthFormat));
38494         this.setYear(d.format(this.yearFormat));
38495         
38496         this.validate();
38497         
38498         return;
38499     },
38500     
38501     setDay : function(v)
38502     {
38503         this.dayField.setValue(v);
38504         this.inputEl.dom.value = this.getValue();
38505         this.validate();
38506         return;
38507     },
38508     
38509     setMonth : function(v)
38510     {
38511         this.monthField.setValue(v, true);
38512         this.inputEl.dom.value = this.getValue();
38513         this.validate();
38514         return;
38515     },
38516     
38517     setYear : function(v)
38518     {
38519         this.yearField.setValue(v);
38520         this.inputEl.dom.value = this.getValue();
38521         this.validate();
38522         return;
38523     },
38524     
38525     getDay : function()
38526     {
38527         return this.dayField.getValue();
38528     },
38529     
38530     getMonth : function()
38531     {
38532         return this.monthField.getValue();
38533     },
38534     
38535     getYear : function()
38536     {
38537         return this.yearField.getValue();
38538     },
38539     
38540     getValue : function()
38541     {
38542         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38543         
38544         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38545         
38546         return date;
38547     },
38548     
38549     reset : function()
38550     {
38551         this.setDay('');
38552         this.setMonth('');
38553         this.setYear('');
38554         this.inputEl.dom.value = '';
38555         this.validate();
38556         return;
38557     },
38558     
38559     validate : function()
38560     {
38561         var d = this.dayField.validate();
38562         var m = this.monthField.validate();
38563         var y = this.yearField.validate();
38564         
38565         var valid = true;
38566         
38567         if(
38568                 (!this.dayAllowBlank && !d) ||
38569                 (!this.monthAllowBlank && !m) ||
38570                 (!this.yearAllowBlank && !y)
38571         ){
38572             valid = false;
38573         }
38574         
38575         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38576             return valid;
38577         }
38578         
38579         if(valid){
38580             this.markValid();
38581             return valid;
38582         }
38583         
38584         this.markInvalid();
38585         
38586         return valid;
38587     },
38588     
38589     markValid : function()
38590     {
38591         
38592         var label = this.el.select('label', true).first();
38593         var icon = this.el.select('i.fa-star', true).first();
38594
38595         if(label && icon){
38596             icon.remove();
38597         }
38598         
38599         this.fireEvent('valid', this);
38600     },
38601     
38602      /**
38603      * Mark this field as invalid
38604      * @param {String} msg The validation message
38605      */
38606     markInvalid : function(msg)
38607     {
38608         
38609         var label = this.el.select('label', true).first();
38610         var icon = this.el.select('i.fa-star', true).first();
38611
38612         if(label && !icon){
38613             this.el.select('.roo-date-split-field-label', true).createChild({
38614                 tag : 'i',
38615                 cls : 'text-danger fa fa-lg fa-star',
38616                 tooltip : 'This field is required',
38617                 style : 'margin-right:5px;'
38618             }, label, true);
38619         }
38620         
38621         this.fireEvent('invalid', this, msg);
38622     },
38623     
38624     clearInvalid : function()
38625     {
38626         var label = this.el.select('label', true).first();
38627         var icon = this.el.select('i.fa-star', true).first();
38628
38629         if(label && icon){
38630             icon.remove();
38631         }
38632         
38633         this.fireEvent('valid', this);
38634     },
38635     
38636     getName: function()
38637     {
38638         return this.name;
38639     }
38640     
38641 });
38642
38643  
38644
38645 /**
38646  * @class Roo.bootstrap.LayoutMasonry
38647  * @extends Roo.bootstrap.Component
38648  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38649  * Bootstrap Layout Masonry class
38650  *
38651  * This is based on 
38652  * http://masonry.desandro.com
38653  *
38654  * The idea is to render all the bricks based on vertical width...
38655  *
38656  * The original code extends 'outlayer' - we might need to use that....
38657
38658  * @constructor
38659  * Create a new Element
38660  * @param {Object} config The config object
38661  */
38662
38663 Roo.bootstrap.LayoutMasonry = function(config){
38664     
38665     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38666     
38667     this.bricks = [];
38668     
38669     Roo.bootstrap.LayoutMasonry.register(this);
38670     
38671     this.addEvents({
38672         // raw events
38673         /**
38674          * @event layout
38675          * Fire after layout the items
38676          * @param {Roo.bootstrap.LayoutMasonry} this
38677          * @param {Roo.EventObject} e
38678          */
38679         "layout" : true
38680     });
38681     
38682 };
38683
38684 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38685     
38686     /**
38687      * @cfg {Boolean} isLayoutInstant = no animation?
38688      */   
38689     isLayoutInstant : false, // needed?
38690    
38691     /**
38692      * @cfg {Number} boxWidth  width of the columns
38693      */   
38694     boxWidth : 450,
38695     
38696       /**
38697      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38698      */   
38699     boxHeight : 0,
38700     
38701     /**
38702      * @cfg {Number} padWidth padding below box..
38703      */   
38704     padWidth : 10, 
38705     
38706     /**
38707      * @cfg {Number} gutter gutter width..
38708      */   
38709     gutter : 10,
38710     
38711      /**
38712      * @cfg {Number} maxCols maximum number of columns
38713      */   
38714     
38715     maxCols: 0,
38716     
38717     /**
38718      * @cfg {Boolean} isAutoInitial defalut true
38719      */   
38720     isAutoInitial : true, 
38721     
38722     containerWidth: 0,
38723     
38724     /**
38725      * @cfg {Boolean} isHorizontal defalut false
38726      */   
38727     isHorizontal : false, 
38728
38729     currentSize : null,
38730     
38731     tag: 'div',
38732     
38733     cls: '',
38734     
38735     bricks: null, //CompositeElement
38736     
38737     cols : 1,
38738     
38739     _isLayoutInited : false,
38740     
38741 //    isAlternative : false, // only use for vertical layout...
38742     
38743     /**
38744      * @cfg {Number} alternativePadWidth padding below box..
38745      */   
38746     alternativePadWidth : 50,
38747     
38748     selectedBrick : [],
38749     
38750     getAutoCreate : function(){
38751         
38752         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38753         
38754         var cfg = {
38755             tag: this.tag,
38756             cls: 'blog-masonary-wrapper ' + this.cls,
38757             cn : {
38758                 cls : 'mas-boxes masonary'
38759             }
38760         };
38761         
38762         return cfg;
38763     },
38764     
38765     getChildContainer: function( )
38766     {
38767         if (this.boxesEl) {
38768             return this.boxesEl;
38769         }
38770         
38771         this.boxesEl = this.el.select('.mas-boxes').first();
38772         
38773         return this.boxesEl;
38774     },
38775     
38776     
38777     initEvents : function()
38778     {
38779         var _this = this;
38780         
38781         if(this.isAutoInitial){
38782             Roo.log('hook children rendered');
38783             this.on('childrenrendered', function() {
38784                 Roo.log('children rendered');
38785                 _this.initial();
38786             } ,this);
38787         }
38788     },
38789     
38790     initial : function()
38791     {
38792         this.selectedBrick = [];
38793         
38794         this.currentSize = this.el.getBox(true);
38795         
38796         Roo.EventManager.onWindowResize(this.resize, this); 
38797
38798         if(!this.isAutoInitial){
38799             this.layout();
38800             return;
38801         }
38802         
38803         this.layout();
38804         
38805         return;
38806         //this.layout.defer(500,this);
38807         
38808     },
38809     
38810     resize : function()
38811     {
38812         var cs = this.el.getBox(true);
38813         
38814         if (
38815                 this.currentSize.width == cs.width && 
38816                 this.currentSize.x == cs.x && 
38817                 this.currentSize.height == cs.height && 
38818                 this.currentSize.y == cs.y 
38819         ) {
38820             Roo.log("no change in with or X or Y");
38821             return;
38822         }
38823         
38824         this.currentSize = cs;
38825         
38826         this.layout();
38827         
38828     },
38829     
38830     layout : function()
38831     {   
38832         this._resetLayout();
38833         
38834         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38835         
38836         this.layoutItems( isInstant );
38837       
38838         this._isLayoutInited = true;
38839         
38840         this.fireEvent('layout', this);
38841         
38842     },
38843     
38844     _resetLayout : function()
38845     {
38846         if(this.isHorizontal){
38847             this.horizontalMeasureColumns();
38848             return;
38849         }
38850         
38851         this.verticalMeasureColumns();
38852         
38853     },
38854     
38855     verticalMeasureColumns : function()
38856     {
38857         this.getContainerWidth();
38858         
38859 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38860 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38861 //            return;
38862 //        }
38863         
38864         var boxWidth = this.boxWidth + this.padWidth;
38865         
38866         if(this.containerWidth < this.boxWidth){
38867             boxWidth = this.containerWidth
38868         }
38869         
38870         var containerWidth = this.containerWidth;
38871         
38872         var cols = Math.floor(containerWidth / boxWidth);
38873         
38874         this.cols = Math.max( cols, 1 );
38875         
38876         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38877         
38878         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38879         
38880         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38881         
38882         this.colWidth = boxWidth + avail - this.padWidth;
38883         
38884         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38885         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38886     },
38887     
38888     horizontalMeasureColumns : function()
38889     {
38890         this.getContainerWidth();
38891         
38892         var boxWidth = this.boxWidth;
38893         
38894         if(this.containerWidth < boxWidth){
38895             boxWidth = this.containerWidth;
38896         }
38897         
38898         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38899         
38900         this.el.setHeight(boxWidth);
38901         
38902     },
38903     
38904     getContainerWidth : function()
38905     {
38906         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38907     },
38908     
38909     layoutItems : function( isInstant )
38910     {
38911         Roo.log(this.bricks);
38912         
38913         var items = Roo.apply([], this.bricks);
38914         
38915         if(this.isHorizontal){
38916             this._horizontalLayoutItems( items , isInstant );
38917             return;
38918         }
38919         
38920 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38921 //            this._verticalAlternativeLayoutItems( items , isInstant );
38922 //            return;
38923 //        }
38924         
38925         this._verticalLayoutItems( items , isInstant );
38926         
38927     },
38928     
38929     _verticalLayoutItems : function ( items , isInstant)
38930     {
38931         if ( !items || !items.length ) {
38932             return;
38933         }
38934         
38935         var standard = [
38936             ['xs', 'xs', 'xs', 'tall'],
38937             ['xs', 'xs', 'tall'],
38938             ['xs', 'xs', 'sm'],
38939             ['xs', 'xs', 'xs'],
38940             ['xs', 'tall'],
38941             ['xs', 'sm'],
38942             ['xs', 'xs'],
38943             ['xs'],
38944             
38945             ['sm', 'xs', 'xs'],
38946             ['sm', 'xs'],
38947             ['sm'],
38948             
38949             ['tall', 'xs', 'xs', 'xs'],
38950             ['tall', 'xs', 'xs'],
38951             ['tall', 'xs'],
38952             ['tall']
38953             
38954         ];
38955         
38956         var queue = [];
38957         
38958         var boxes = [];
38959         
38960         var box = [];
38961         
38962         Roo.each(items, function(item, k){
38963             
38964             switch (item.size) {
38965                 // these layouts take up a full box,
38966                 case 'md' :
38967                 case 'md-left' :
38968                 case 'md-right' :
38969                 case 'wide' :
38970                     
38971                     if(box.length){
38972                         boxes.push(box);
38973                         box = [];
38974                     }
38975                     
38976                     boxes.push([item]);
38977                     
38978                     break;
38979                     
38980                 case 'xs' :
38981                 case 'sm' :
38982                 case 'tall' :
38983                     
38984                     box.push(item);
38985                     
38986                     break;
38987                 default :
38988                     break;
38989                     
38990             }
38991             
38992         }, this);
38993         
38994         if(box.length){
38995             boxes.push(box);
38996             box = [];
38997         }
38998         
38999         var filterPattern = function(box, length)
39000         {
39001             if(!box.length){
39002                 return;
39003             }
39004             
39005             var match = false;
39006             
39007             var pattern = box.slice(0, length);
39008             
39009             var format = [];
39010             
39011             Roo.each(pattern, function(i){
39012                 format.push(i.size);
39013             }, this);
39014             
39015             Roo.each(standard, function(s){
39016                 
39017                 if(String(s) != String(format)){
39018                     return;
39019                 }
39020                 
39021                 match = true;
39022                 return false;
39023                 
39024             }, this);
39025             
39026             if(!match && length == 1){
39027                 return;
39028             }
39029             
39030             if(!match){
39031                 filterPattern(box, length - 1);
39032                 return;
39033             }
39034                 
39035             queue.push(pattern);
39036
39037             box = box.slice(length, box.length);
39038
39039             filterPattern(box, 4);
39040
39041             return;
39042             
39043         }
39044         
39045         Roo.each(boxes, function(box, k){
39046             
39047             if(!box.length){
39048                 return;
39049             }
39050             
39051             if(box.length == 1){
39052                 queue.push(box);
39053                 return;
39054             }
39055             
39056             filterPattern(box, 4);
39057             
39058         }, this);
39059         
39060         this._processVerticalLayoutQueue( queue, isInstant );
39061         
39062     },
39063     
39064 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39065 //    {
39066 //        if ( !items || !items.length ) {
39067 //            return;
39068 //        }
39069 //
39070 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39071 //        
39072 //    },
39073     
39074     _horizontalLayoutItems : function ( items , isInstant)
39075     {
39076         if ( !items || !items.length || items.length < 3) {
39077             return;
39078         }
39079         
39080         items.reverse();
39081         
39082         var eItems = items.slice(0, 3);
39083         
39084         items = items.slice(3, items.length);
39085         
39086         var standard = [
39087             ['xs', 'xs', 'xs', 'wide'],
39088             ['xs', 'xs', 'wide'],
39089             ['xs', 'xs', 'sm'],
39090             ['xs', 'xs', 'xs'],
39091             ['xs', 'wide'],
39092             ['xs', 'sm'],
39093             ['xs', 'xs'],
39094             ['xs'],
39095             
39096             ['sm', 'xs', 'xs'],
39097             ['sm', 'xs'],
39098             ['sm'],
39099             
39100             ['wide', 'xs', 'xs', 'xs'],
39101             ['wide', 'xs', 'xs'],
39102             ['wide', 'xs'],
39103             ['wide'],
39104             
39105             ['wide-thin']
39106         ];
39107         
39108         var queue = [];
39109         
39110         var boxes = [];
39111         
39112         var box = [];
39113         
39114         Roo.each(items, function(item, k){
39115             
39116             switch (item.size) {
39117                 case 'md' :
39118                 case 'md-left' :
39119                 case 'md-right' :
39120                 case 'tall' :
39121                     
39122                     if(box.length){
39123                         boxes.push(box);
39124                         box = [];
39125                     }
39126                     
39127                     boxes.push([item]);
39128                     
39129                     break;
39130                     
39131                 case 'xs' :
39132                 case 'sm' :
39133                 case 'wide' :
39134                 case 'wide-thin' :
39135                     
39136                     box.push(item);
39137                     
39138                     break;
39139                 default :
39140                     break;
39141                     
39142             }
39143             
39144         }, this);
39145         
39146         if(box.length){
39147             boxes.push(box);
39148             box = [];
39149         }
39150         
39151         var filterPattern = function(box, length)
39152         {
39153             if(!box.length){
39154                 return;
39155             }
39156             
39157             var match = false;
39158             
39159             var pattern = box.slice(0, length);
39160             
39161             var format = [];
39162             
39163             Roo.each(pattern, function(i){
39164                 format.push(i.size);
39165             }, this);
39166             
39167             Roo.each(standard, function(s){
39168                 
39169                 if(String(s) != String(format)){
39170                     return;
39171                 }
39172                 
39173                 match = true;
39174                 return false;
39175                 
39176             }, this);
39177             
39178             if(!match && length == 1){
39179                 return;
39180             }
39181             
39182             if(!match){
39183                 filterPattern(box, length - 1);
39184                 return;
39185             }
39186                 
39187             queue.push(pattern);
39188
39189             box = box.slice(length, box.length);
39190
39191             filterPattern(box, 4);
39192
39193             return;
39194             
39195         }
39196         
39197         Roo.each(boxes, function(box, k){
39198             
39199             if(!box.length){
39200                 return;
39201             }
39202             
39203             if(box.length == 1){
39204                 queue.push(box);
39205                 return;
39206             }
39207             
39208             filterPattern(box, 4);
39209             
39210         }, this);
39211         
39212         
39213         var prune = [];
39214         
39215         var pos = this.el.getBox(true);
39216         
39217         var minX = pos.x;
39218         
39219         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39220         
39221         var hit_end = false;
39222         
39223         Roo.each(queue, function(box){
39224             
39225             if(hit_end){
39226                 
39227                 Roo.each(box, function(b){
39228                 
39229                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39230                     b.el.hide();
39231
39232                 }, this);
39233
39234                 return;
39235             }
39236             
39237             var mx = 0;
39238             
39239             Roo.each(box, function(b){
39240                 
39241                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39242                 b.el.show();
39243
39244                 mx = Math.max(mx, b.x);
39245                 
39246             }, this);
39247             
39248             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39249             
39250             if(maxX < minX){
39251                 
39252                 Roo.each(box, function(b){
39253                 
39254                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39255                     b.el.hide();
39256                     
39257                 }, this);
39258                 
39259                 hit_end = true;
39260                 
39261                 return;
39262             }
39263             
39264             prune.push(box);
39265             
39266         }, this);
39267         
39268         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39269     },
39270     
39271     /** Sets position of item in DOM
39272     * @param {Element} item
39273     * @param {Number} x - horizontal position
39274     * @param {Number} y - vertical position
39275     * @param {Boolean} isInstant - disables transitions
39276     */
39277     _processVerticalLayoutQueue : function( queue, isInstant )
39278     {
39279         var pos = this.el.getBox(true);
39280         var x = pos.x;
39281         var y = pos.y;
39282         var maxY = [];
39283         
39284         for (var i = 0; i < this.cols; i++){
39285             maxY[i] = pos.y;
39286         }
39287         
39288         Roo.each(queue, function(box, k){
39289             
39290             var col = k % this.cols;
39291             
39292             Roo.each(box, function(b,kk){
39293                 
39294                 b.el.position('absolute');
39295                 
39296                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39297                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39298                 
39299                 if(b.size == 'md-left' || b.size == 'md-right'){
39300                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39301                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39302                 }
39303                 
39304                 b.el.setWidth(width);
39305                 b.el.setHeight(height);
39306                 // iframe?
39307                 b.el.select('iframe',true).setSize(width,height);
39308                 
39309             }, this);
39310             
39311             for (var i = 0; i < this.cols; i++){
39312                 
39313                 if(maxY[i] < maxY[col]){
39314                     col = i;
39315                     continue;
39316                 }
39317                 
39318                 col = Math.min(col, i);
39319                 
39320             }
39321             
39322             x = pos.x + col * (this.colWidth + this.padWidth);
39323             
39324             y = maxY[col];
39325             
39326             var positions = [];
39327             
39328             switch (box.length){
39329                 case 1 :
39330                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39331                     break;
39332                 case 2 :
39333                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39334                     break;
39335                 case 3 :
39336                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39337                     break;
39338                 case 4 :
39339                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39340                     break;
39341                 default :
39342                     break;
39343             }
39344             
39345             Roo.each(box, function(b,kk){
39346                 
39347                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39348                 
39349                 var sz = b.el.getSize();
39350                 
39351                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39352                 
39353             }, this);
39354             
39355         }, this);
39356         
39357         var mY = 0;
39358         
39359         for (var i = 0; i < this.cols; i++){
39360             mY = Math.max(mY, maxY[i]);
39361         }
39362         
39363         this.el.setHeight(mY - pos.y);
39364         
39365     },
39366     
39367 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39368 //    {
39369 //        var pos = this.el.getBox(true);
39370 //        var x = pos.x;
39371 //        var y = pos.y;
39372 //        var maxX = pos.right;
39373 //        
39374 //        var maxHeight = 0;
39375 //        
39376 //        Roo.each(items, function(item, k){
39377 //            
39378 //            var c = k % 2;
39379 //            
39380 //            item.el.position('absolute');
39381 //                
39382 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39383 //
39384 //            item.el.setWidth(width);
39385 //
39386 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39387 //
39388 //            item.el.setHeight(height);
39389 //            
39390 //            if(c == 0){
39391 //                item.el.setXY([x, y], isInstant ? false : true);
39392 //            } else {
39393 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39394 //            }
39395 //            
39396 //            y = y + height + this.alternativePadWidth;
39397 //            
39398 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39399 //            
39400 //        }, this);
39401 //        
39402 //        this.el.setHeight(maxHeight);
39403 //        
39404 //    },
39405     
39406     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39407     {
39408         var pos = this.el.getBox(true);
39409         
39410         var minX = pos.x;
39411         var minY = pos.y;
39412         
39413         var maxX = pos.right;
39414         
39415         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39416         
39417         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39418         
39419         Roo.each(queue, function(box, k){
39420             
39421             Roo.each(box, function(b, kk){
39422                 
39423                 b.el.position('absolute');
39424                 
39425                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39426                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39427                 
39428                 if(b.size == 'md-left' || b.size == 'md-right'){
39429                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39430                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39431                 }
39432                 
39433                 b.el.setWidth(width);
39434                 b.el.setHeight(height);
39435                 
39436             }, this);
39437             
39438             if(!box.length){
39439                 return;
39440             }
39441             
39442             var positions = [];
39443             
39444             switch (box.length){
39445                 case 1 :
39446                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39447                     break;
39448                 case 2 :
39449                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39450                     break;
39451                 case 3 :
39452                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39453                     break;
39454                 case 4 :
39455                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39456                     break;
39457                 default :
39458                     break;
39459             }
39460             
39461             Roo.each(box, function(b,kk){
39462                 
39463                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39464                 
39465                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39466                 
39467             }, this);
39468             
39469         }, this);
39470         
39471     },
39472     
39473     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39474     {
39475         Roo.each(eItems, function(b,k){
39476             
39477             b.size = (k == 0) ? 'sm' : 'xs';
39478             b.x = (k == 0) ? 2 : 1;
39479             b.y = (k == 0) ? 2 : 1;
39480             
39481             b.el.position('absolute');
39482             
39483             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39484                 
39485             b.el.setWidth(width);
39486             
39487             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39488             
39489             b.el.setHeight(height);
39490             
39491         }, this);
39492
39493         var positions = [];
39494         
39495         positions.push({
39496             x : maxX - this.unitWidth * 2 - this.gutter,
39497             y : minY
39498         });
39499         
39500         positions.push({
39501             x : maxX - this.unitWidth,
39502             y : minY + (this.unitWidth + this.gutter) * 2
39503         });
39504         
39505         positions.push({
39506             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39507             y : minY
39508         });
39509         
39510         Roo.each(eItems, function(b,k){
39511             
39512             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39513
39514         }, this);
39515         
39516     },
39517     
39518     getVerticalOneBoxColPositions : function(x, y, box)
39519     {
39520         var pos = [];
39521         
39522         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39523         
39524         if(box[0].size == 'md-left'){
39525             rand = 0;
39526         }
39527         
39528         if(box[0].size == 'md-right'){
39529             rand = 1;
39530         }
39531         
39532         pos.push({
39533             x : x + (this.unitWidth + this.gutter) * rand,
39534             y : y
39535         });
39536         
39537         return pos;
39538     },
39539     
39540     getVerticalTwoBoxColPositions : function(x, y, box)
39541     {
39542         var pos = [];
39543         
39544         if(box[0].size == 'xs'){
39545             
39546             pos.push({
39547                 x : x,
39548                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39549             });
39550
39551             pos.push({
39552                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39553                 y : y
39554             });
39555             
39556             return pos;
39557             
39558         }
39559         
39560         pos.push({
39561             x : x,
39562             y : y
39563         });
39564
39565         pos.push({
39566             x : x + (this.unitWidth + this.gutter) * 2,
39567             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39568         });
39569         
39570         return pos;
39571         
39572     },
39573     
39574     getVerticalThreeBoxColPositions : function(x, y, box)
39575     {
39576         var pos = [];
39577         
39578         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39579             
39580             pos.push({
39581                 x : x,
39582                 y : y
39583             });
39584
39585             pos.push({
39586                 x : x + (this.unitWidth + this.gutter) * 1,
39587                 y : y
39588             });
39589             
39590             pos.push({
39591                 x : x + (this.unitWidth + this.gutter) * 2,
39592                 y : y
39593             });
39594             
39595             return pos;
39596             
39597         }
39598         
39599         if(box[0].size == 'xs' && box[1].size == 'xs'){
39600             
39601             pos.push({
39602                 x : x,
39603                 y : y
39604             });
39605
39606             pos.push({
39607                 x : x,
39608                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39609             });
39610             
39611             pos.push({
39612                 x : x + (this.unitWidth + this.gutter) * 1,
39613                 y : y
39614             });
39615             
39616             return pos;
39617             
39618         }
39619         
39620         pos.push({
39621             x : x,
39622             y : y
39623         });
39624
39625         pos.push({
39626             x : x + (this.unitWidth + this.gutter) * 2,
39627             y : y
39628         });
39629
39630         pos.push({
39631             x : x + (this.unitWidth + this.gutter) * 2,
39632             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39633         });
39634             
39635         return pos;
39636         
39637     },
39638     
39639     getVerticalFourBoxColPositions : function(x, y, box)
39640     {
39641         var pos = [];
39642         
39643         if(box[0].size == 'xs'){
39644             
39645             pos.push({
39646                 x : x,
39647                 y : y
39648             });
39649
39650             pos.push({
39651                 x : x,
39652                 y : y + (this.unitHeight + this.gutter) * 1
39653             });
39654             
39655             pos.push({
39656                 x : x,
39657                 y : y + (this.unitHeight + this.gutter) * 2
39658             });
39659             
39660             pos.push({
39661                 x : x + (this.unitWidth + this.gutter) * 1,
39662                 y : y
39663             });
39664             
39665             return pos;
39666             
39667         }
39668         
39669         pos.push({
39670             x : x,
39671             y : y
39672         });
39673
39674         pos.push({
39675             x : x + (this.unitWidth + this.gutter) * 2,
39676             y : y
39677         });
39678
39679         pos.push({
39680             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39681             y : y + (this.unitHeight + this.gutter) * 1
39682         });
39683
39684         pos.push({
39685             x : x + (this.unitWidth + this.gutter) * 2,
39686             y : y + (this.unitWidth + this.gutter) * 2
39687         });
39688
39689         return pos;
39690         
39691     },
39692     
39693     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39694     {
39695         var pos = [];
39696         
39697         if(box[0].size == 'md-left'){
39698             pos.push({
39699                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39700                 y : minY
39701             });
39702             
39703             return pos;
39704         }
39705         
39706         if(box[0].size == 'md-right'){
39707             pos.push({
39708                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39709                 y : minY + (this.unitWidth + this.gutter) * 1
39710             });
39711             
39712             return pos;
39713         }
39714         
39715         var rand = Math.floor(Math.random() * (4 - box[0].y));
39716         
39717         pos.push({
39718             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39719             y : minY + (this.unitWidth + this.gutter) * rand
39720         });
39721         
39722         return pos;
39723         
39724     },
39725     
39726     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39727     {
39728         var pos = [];
39729         
39730         if(box[0].size == 'xs'){
39731             
39732             pos.push({
39733                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39734                 y : minY
39735             });
39736
39737             pos.push({
39738                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39739                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39740             });
39741             
39742             return pos;
39743             
39744         }
39745         
39746         pos.push({
39747             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39748             y : minY
39749         });
39750
39751         pos.push({
39752             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39753             y : minY + (this.unitWidth + this.gutter) * 2
39754         });
39755         
39756         return pos;
39757         
39758     },
39759     
39760     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39761     {
39762         var pos = [];
39763         
39764         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39765             
39766             pos.push({
39767                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39768                 y : minY
39769             });
39770
39771             pos.push({
39772                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39773                 y : minY + (this.unitWidth + this.gutter) * 1
39774             });
39775             
39776             pos.push({
39777                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39778                 y : minY + (this.unitWidth + this.gutter) * 2
39779             });
39780             
39781             return pos;
39782             
39783         }
39784         
39785         if(box[0].size == 'xs' && box[1].size == 'xs'){
39786             
39787             pos.push({
39788                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39789                 y : minY
39790             });
39791
39792             pos.push({
39793                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39794                 y : minY
39795             });
39796             
39797             pos.push({
39798                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39799                 y : minY + (this.unitWidth + this.gutter) * 1
39800             });
39801             
39802             return pos;
39803             
39804         }
39805         
39806         pos.push({
39807             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39808             y : minY
39809         });
39810
39811         pos.push({
39812             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39813             y : minY + (this.unitWidth + this.gutter) * 2
39814         });
39815
39816         pos.push({
39817             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39818             y : minY + (this.unitWidth + this.gutter) * 2
39819         });
39820             
39821         return pos;
39822         
39823     },
39824     
39825     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39826     {
39827         var pos = [];
39828         
39829         if(box[0].size == 'xs'){
39830             
39831             pos.push({
39832                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39833                 y : minY
39834             });
39835
39836             pos.push({
39837                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39838                 y : minY
39839             });
39840             
39841             pos.push({
39842                 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),
39843                 y : minY
39844             });
39845             
39846             pos.push({
39847                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39848                 y : minY + (this.unitWidth + this.gutter) * 1
39849             });
39850             
39851             return pos;
39852             
39853         }
39854         
39855         pos.push({
39856             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39857             y : minY
39858         });
39859         
39860         pos.push({
39861             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39862             y : minY + (this.unitWidth + this.gutter) * 2
39863         });
39864         
39865         pos.push({
39866             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39867             y : minY + (this.unitWidth + this.gutter) * 2
39868         });
39869         
39870         pos.push({
39871             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),
39872             y : minY + (this.unitWidth + this.gutter) * 2
39873         });
39874
39875         return pos;
39876         
39877     },
39878     
39879     /**
39880     * remove a Masonry Brick
39881     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39882     */
39883     removeBrick : function(brick_id)
39884     {
39885         if (!brick_id) {
39886             return;
39887         }
39888         
39889         for (var i = 0; i<this.bricks.length; i++) {
39890             if (this.bricks[i].id == brick_id) {
39891                 this.bricks.splice(i,1);
39892                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39893                 this.initial();
39894             }
39895         }
39896     },
39897     
39898     /**
39899     * adds a Masonry Brick
39900     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39901     */
39902     addBrick : function(cfg)
39903     {
39904         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39905         //this.register(cn);
39906         cn.parentId = this.id;
39907         cn.render(this.el);
39908         return cn;
39909     },
39910     
39911     /**
39912     * register a Masonry Brick
39913     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39914     */
39915     
39916     register : function(brick)
39917     {
39918         this.bricks.push(brick);
39919         brick.masonryId = this.id;
39920     },
39921     
39922     /**
39923     * clear all the Masonry Brick
39924     */
39925     clearAll : function()
39926     {
39927         this.bricks = [];
39928         //this.getChildContainer().dom.innerHTML = "";
39929         this.el.dom.innerHTML = '';
39930     },
39931     
39932     getSelected : function()
39933     {
39934         if (!this.selectedBrick) {
39935             return false;
39936         }
39937         
39938         return this.selectedBrick;
39939     }
39940 });
39941
39942 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39943     
39944     groups: {},
39945      /**
39946     * register a Masonry Layout
39947     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39948     */
39949     
39950     register : function(layout)
39951     {
39952         this.groups[layout.id] = layout;
39953     },
39954     /**
39955     * fetch a  Masonry Layout based on the masonry layout ID
39956     * @param {string} the masonry layout to add
39957     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39958     */
39959     
39960     get: function(layout_id) {
39961         if (typeof(this.groups[layout_id]) == 'undefined') {
39962             return false;
39963         }
39964         return this.groups[layout_id] ;
39965     }
39966     
39967     
39968     
39969 });
39970
39971  
39972
39973  /**
39974  *
39975  * This is based on 
39976  * http://masonry.desandro.com
39977  *
39978  * The idea is to render all the bricks based on vertical width...
39979  *
39980  * The original code extends 'outlayer' - we might need to use that....
39981  * 
39982  */
39983
39984
39985 /**
39986  * @class Roo.bootstrap.LayoutMasonryAuto
39987  * @extends Roo.bootstrap.Component
39988  * Bootstrap Layout Masonry class
39989  * 
39990  * @constructor
39991  * Create a new Element
39992  * @param {Object} config The config object
39993  */
39994
39995 Roo.bootstrap.LayoutMasonryAuto = function(config){
39996     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39997 };
39998
39999 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40000     
40001       /**
40002      * @cfg {Boolean} isFitWidth  - resize the width..
40003      */   
40004     isFitWidth : false,  // options..
40005     /**
40006      * @cfg {Boolean} isOriginLeft = left align?
40007      */   
40008     isOriginLeft : true,
40009     /**
40010      * @cfg {Boolean} isOriginTop = top align?
40011      */   
40012     isOriginTop : false,
40013     /**
40014      * @cfg {Boolean} isLayoutInstant = no animation?
40015      */   
40016     isLayoutInstant : false, // needed?
40017     /**
40018      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40019      */   
40020     isResizingContainer : true,
40021     /**
40022      * @cfg {Number} columnWidth  width of the columns 
40023      */   
40024     
40025     columnWidth : 0,
40026     
40027     /**
40028      * @cfg {Number} maxCols maximum number of columns
40029      */   
40030     
40031     maxCols: 0,
40032     /**
40033      * @cfg {Number} padHeight padding below box..
40034      */   
40035     
40036     padHeight : 10, 
40037     
40038     /**
40039      * @cfg {Boolean} isAutoInitial defalut true
40040      */   
40041     
40042     isAutoInitial : true, 
40043     
40044     // private?
40045     gutter : 0,
40046     
40047     containerWidth: 0,
40048     initialColumnWidth : 0,
40049     currentSize : null,
40050     
40051     colYs : null, // array.
40052     maxY : 0,
40053     padWidth: 10,
40054     
40055     
40056     tag: 'div',
40057     cls: '',
40058     bricks: null, //CompositeElement
40059     cols : 0, // array?
40060     // element : null, // wrapped now this.el
40061     _isLayoutInited : null, 
40062     
40063     
40064     getAutoCreate : function(){
40065         
40066         var cfg = {
40067             tag: this.tag,
40068             cls: 'blog-masonary-wrapper ' + this.cls,
40069             cn : {
40070                 cls : 'mas-boxes masonary'
40071             }
40072         };
40073         
40074         return cfg;
40075     },
40076     
40077     getChildContainer: function( )
40078     {
40079         if (this.boxesEl) {
40080             return this.boxesEl;
40081         }
40082         
40083         this.boxesEl = this.el.select('.mas-boxes').first();
40084         
40085         return this.boxesEl;
40086     },
40087     
40088     
40089     initEvents : function()
40090     {
40091         var _this = this;
40092         
40093         if(this.isAutoInitial){
40094             Roo.log('hook children rendered');
40095             this.on('childrenrendered', function() {
40096                 Roo.log('children rendered');
40097                 _this.initial();
40098             } ,this);
40099         }
40100         
40101     },
40102     
40103     initial : function()
40104     {
40105         this.reloadItems();
40106
40107         this.currentSize = this.el.getBox(true);
40108
40109         /// was window resize... - let's see if this works..
40110         Roo.EventManager.onWindowResize(this.resize, this); 
40111
40112         if(!this.isAutoInitial){
40113             this.layout();
40114             return;
40115         }
40116         
40117         this.layout.defer(500,this);
40118     },
40119     
40120     reloadItems: function()
40121     {
40122         this.bricks = this.el.select('.masonry-brick', true);
40123         
40124         this.bricks.each(function(b) {
40125             //Roo.log(b.getSize());
40126             if (!b.attr('originalwidth')) {
40127                 b.attr('originalwidth',  b.getSize().width);
40128             }
40129             
40130         });
40131         
40132         Roo.log(this.bricks.elements.length);
40133     },
40134     
40135     resize : function()
40136     {
40137         Roo.log('resize');
40138         var cs = this.el.getBox(true);
40139         
40140         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40141             Roo.log("no change in with or X");
40142             return;
40143         }
40144         this.currentSize = cs;
40145         this.layout();
40146     },
40147     
40148     layout : function()
40149     {
40150          Roo.log('layout');
40151         this._resetLayout();
40152         //this._manageStamps();
40153       
40154         // don't animate first layout
40155         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40156         this.layoutItems( isInstant );
40157       
40158         // flag for initalized
40159         this._isLayoutInited = true;
40160     },
40161     
40162     layoutItems : function( isInstant )
40163     {
40164         //var items = this._getItemsForLayout( this.items );
40165         // original code supports filtering layout items.. we just ignore it..
40166         
40167         this._layoutItems( this.bricks , isInstant );
40168       
40169         this._postLayout();
40170     },
40171     _layoutItems : function ( items , isInstant)
40172     {
40173        //this.fireEvent( 'layout', this, items );
40174     
40175
40176         if ( !items || !items.elements.length ) {
40177           // no items, emit event with empty array
40178             return;
40179         }
40180
40181         var queue = [];
40182         items.each(function(item) {
40183             Roo.log("layout item");
40184             Roo.log(item);
40185             // get x/y object from method
40186             var position = this._getItemLayoutPosition( item );
40187             // enqueue
40188             position.item = item;
40189             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40190             queue.push( position );
40191         }, this);
40192       
40193         this._processLayoutQueue( queue );
40194     },
40195     /** Sets position of item in DOM
40196     * @param {Element} item
40197     * @param {Number} x - horizontal position
40198     * @param {Number} y - vertical position
40199     * @param {Boolean} isInstant - disables transitions
40200     */
40201     _processLayoutQueue : function( queue )
40202     {
40203         for ( var i=0, len = queue.length; i < len; i++ ) {
40204             var obj = queue[i];
40205             obj.item.position('absolute');
40206             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40207         }
40208     },
40209       
40210     
40211     /**
40212     * Any logic you want to do after each layout,
40213     * i.e. size the container
40214     */
40215     _postLayout : function()
40216     {
40217         this.resizeContainer();
40218     },
40219     
40220     resizeContainer : function()
40221     {
40222         if ( !this.isResizingContainer ) {
40223             return;
40224         }
40225         var size = this._getContainerSize();
40226         if ( size ) {
40227             this.el.setSize(size.width,size.height);
40228             this.boxesEl.setSize(size.width,size.height);
40229         }
40230     },
40231     
40232     
40233     
40234     _resetLayout : function()
40235     {
40236         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40237         this.colWidth = this.el.getWidth();
40238         //this.gutter = this.el.getWidth(); 
40239         
40240         this.measureColumns();
40241
40242         // reset column Y
40243         var i = this.cols;
40244         this.colYs = [];
40245         while (i--) {
40246             this.colYs.push( 0 );
40247         }
40248     
40249         this.maxY = 0;
40250     },
40251
40252     measureColumns : function()
40253     {
40254         this.getContainerWidth();
40255       // if columnWidth is 0, default to outerWidth of first item
40256         if ( !this.columnWidth ) {
40257             var firstItem = this.bricks.first();
40258             Roo.log(firstItem);
40259             this.columnWidth  = this.containerWidth;
40260             if (firstItem && firstItem.attr('originalwidth') ) {
40261                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40262             }
40263             // columnWidth fall back to item of first element
40264             Roo.log("set column width?");
40265                         this.initialColumnWidth = this.columnWidth  ;
40266
40267             // if first elem has no width, default to size of container
40268             
40269         }
40270         
40271         
40272         if (this.initialColumnWidth) {
40273             this.columnWidth = this.initialColumnWidth;
40274         }
40275         
40276         
40277             
40278         // column width is fixed at the top - however if container width get's smaller we should
40279         // reduce it...
40280         
40281         // this bit calcs how man columns..
40282             
40283         var columnWidth = this.columnWidth += this.gutter;
40284       
40285         // calculate columns
40286         var containerWidth = this.containerWidth + this.gutter;
40287         
40288         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40289         // fix rounding errors, typically with gutters
40290         var excess = columnWidth - containerWidth % columnWidth;
40291         
40292         
40293         // if overshoot is less than a pixel, round up, otherwise floor it
40294         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40295         cols = Math[ mathMethod ]( cols );
40296         this.cols = Math.max( cols, 1 );
40297         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40298         
40299          // padding positioning..
40300         var totalColWidth = this.cols * this.columnWidth;
40301         var padavail = this.containerWidth - totalColWidth;
40302         // so for 2 columns - we need 3 'pads'
40303         
40304         var padNeeded = (1+this.cols) * this.padWidth;
40305         
40306         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40307         
40308         this.columnWidth += padExtra
40309         //this.padWidth = Math.floor(padavail /  ( this.cols));
40310         
40311         // adjust colum width so that padding is fixed??
40312         
40313         // we have 3 columns ... total = width * 3
40314         // we have X left over... that should be used by 
40315         
40316         //if (this.expandC) {
40317             
40318         //}
40319         
40320         
40321         
40322     },
40323     
40324     getContainerWidth : function()
40325     {
40326        /* // container is parent if fit width
40327         var container = this.isFitWidth ? this.element.parentNode : this.element;
40328         // check that this.size and size are there
40329         // IE8 triggers resize on body size change, so they might not be
40330         
40331         var size = getSize( container );  //FIXME
40332         this.containerWidth = size && size.innerWidth; //FIXME
40333         */
40334          
40335         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40336         
40337     },
40338     
40339     _getItemLayoutPosition : function( item )  // what is item?
40340     {
40341         // we resize the item to our columnWidth..
40342       
40343         item.setWidth(this.columnWidth);
40344         item.autoBoxAdjust  = false;
40345         
40346         var sz = item.getSize();
40347  
40348         // how many columns does this brick span
40349         var remainder = this.containerWidth % this.columnWidth;
40350         
40351         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40352         // round if off by 1 pixel, otherwise use ceil
40353         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40354         colSpan = Math.min( colSpan, this.cols );
40355         
40356         // normally this should be '1' as we dont' currently allow multi width columns..
40357         
40358         var colGroup = this._getColGroup( colSpan );
40359         // get the minimum Y value from the columns
40360         var minimumY = Math.min.apply( Math, colGroup );
40361         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40362         
40363         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40364          
40365         // position the brick
40366         var position = {
40367             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40368             y: this.currentSize.y + minimumY + this.padHeight
40369         };
40370         
40371         Roo.log(position);
40372         // apply setHeight to necessary columns
40373         var setHeight = minimumY + sz.height + this.padHeight;
40374         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40375         
40376         var setSpan = this.cols + 1 - colGroup.length;
40377         for ( var i = 0; i < setSpan; i++ ) {
40378           this.colYs[ shortColIndex + i ] = setHeight ;
40379         }
40380       
40381         return position;
40382     },
40383     
40384     /**
40385      * @param {Number} colSpan - number of columns the element spans
40386      * @returns {Array} colGroup
40387      */
40388     _getColGroup : function( colSpan )
40389     {
40390         if ( colSpan < 2 ) {
40391           // if brick spans only one column, use all the column Ys
40392           return this.colYs;
40393         }
40394       
40395         var colGroup = [];
40396         // how many different places could this brick fit horizontally
40397         var groupCount = this.cols + 1 - colSpan;
40398         // for each group potential horizontal position
40399         for ( var i = 0; i < groupCount; i++ ) {
40400           // make an array of colY values for that one group
40401           var groupColYs = this.colYs.slice( i, i + colSpan );
40402           // and get the max value of the array
40403           colGroup[i] = Math.max.apply( Math, groupColYs );
40404         }
40405         return colGroup;
40406     },
40407     /*
40408     _manageStamp : function( stamp )
40409     {
40410         var stampSize =  stamp.getSize();
40411         var offset = stamp.getBox();
40412         // get the columns that this stamp affects
40413         var firstX = this.isOriginLeft ? offset.x : offset.right;
40414         var lastX = firstX + stampSize.width;
40415         var firstCol = Math.floor( firstX / this.columnWidth );
40416         firstCol = Math.max( 0, firstCol );
40417         
40418         var lastCol = Math.floor( lastX / this.columnWidth );
40419         // lastCol should not go over if multiple of columnWidth #425
40420         lastCol -= lastX % this.columnWidth ? 0 : 1;
40421         lastCol = Math.min( this.cols - 1, lastCol );
40422         
40423         // set colYs to bottom of the stamp
40424         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40425             stampSize.height;
40426             
40427         for ( var i = firstCol; i <= lastCol; i++ ) {
40428           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40429         }
40430     },
40431     */
40432     
40433     _getContainerSize : function()
40434     {
40435         this.maxY = Math.max.apply( Math, this.colYs );
40436         var size = {
40437             height: this.maxY
40438         };
40439       
40440         if ( this.isFitWidth ) {
40441             size.width = this._getContainerFitWidth();
40442         }
40443       
40444         return size;
40445     },
40446     
40447     _getContainerFitWidth : function()
40448     {
40449         var unusedCols = 0;
40450         // count unused columns
40451         var i = this.cols;
40452         while ( --i ) {
40453           if ( this.colYs[i] !== 0 ) {
40454             break;
40455           }
40456           unusedCols++;
40457         }
40458         // fit container to columns that have been used
40459         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40460     },
40461     
40462     needsResizeLayout : function()
40463     {
40464         var previousWidth = this.containerWidth;
40465         this.getContainerWidth();
40466         return previousWidth !== this.containerWidth;
40467     }
40468  
40469 });
40470
40471  
40472
40473  /*
40474  * - LGPL
40475  *
40476  * element
40477  * 
40478  */
40479
40480 /**
40481  * @class Roo.bootstrap.MasonryBrick
40482  * @extends Roo.bootstrap.Component
40483  * Bootstrap MasonryBrick class
40484  * 
40485  * @constructor
40486  * Create a new MasonryBrick
40487  * @param {Object} config The config object
40488  */
40489
40490 Roo.bootstrap.MasonryBrick = function(config){
40491     
40492     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40493     
40494     Roo.bootstrap.MasonryBrick.register(this);
40495     
40496     this.addEvents({
40497         // raw events
40498         /**
40499          * @event click
40500          * When a MasonryBrick is clcik
40501          * @param {Roo.bootstrap.MasonryBrick} this
40502          * @param {Roo.EventObject} e
40503          */
40504         "click" : true
40505     });
40506 };
40507
40508 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40509     
40510     /**
40511      * @cfg {String} title
40512      */   
40513     title : '',
40514     /**
40515      * @cfg {String} html
40516      */   
40517     html : '',
40518     /**
40519      * @cfg {String} bgimage
40520      */   
40521     bgimage : '',
40522     /**
40523      * @cfg {String} videourl
40524      */   
40525     videourl : '',
40526     /**
40527      * @cfg {String} cls
40528      */   
40529     cls : '',
40530     /**
40531      * @cfg {String} href
40532      */   
40533     href : '',
40534     /**
40535      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40536      */   
40537     size : 'xs',
40538     
40539     /**
40540      * @cfg {String} placetitle (center|bottom)
40541      */   
40542     placetitle : '',
40543     
40544     /**
40545      * @cfg {Boolean} isFitContainer defalut true
40546      */   
40547     isFitContainer : true, 
40548     
40549     /**
40550      * @cfg {Boolean} preventDefault defalut false
40551      */   
40552     preventDefault : false, 
40553     
40554     /**
40555      * @cfg {Boolean} inverse defalut false
40556      */   
40557     maskInverse : false, 
40558     
40559     getAutoCreate : function()
40560     {
40561         if(!this.isFitContainer){
40562             return this.getSplitAutoCreate();
40563         }
40564         
40565         var cls = 'masonry-brick masonry-brick-full';
40566         
40567         if(this.href.length){
40568             cls += ' masonry-brick-link';
40569         }
40570         
40571         if(this.bgimage.length){
40572             cls += ' masonry-brick-image';
40573         }
40574         
40575         if(this.maskInverse){
40576             cls += ' mask-inverse';
40577         }
40578         
40579         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40580             cls += ' enable-mask';
40581         }
40582         
40583         if(this.size){
40584             cls += ' masonry-' + this.size + '-brick';
40585         }
40586         
40587         if(this.placetitle.length){
40588             
40589             switch (this.placetitle) {
40590                 case 'center' :
40591                     cls += ' masonry-center-title';
40592                     break;
40593                 case 'bottom' :
40594                     cls += ' masonry-bottom-title';
40595                     break;
40596                 default:
40597                     break;
40598             }
40599             
40600         } else {
40601             if(!this.html.length && !this.bgimage.length){
40602                 cls += ' masonry-center-title';
40603             }
40604
40605             if(!this.html.length && this.bgimage.length){
40606                 cls += ' masonry-bottom-title';
40607             }
40608         }
40609         
40610         if(this.cls){
40611             cls += ' ' + this.cls;
40612         }
40613         
40614         var cfg = {
40615             tag: (this.href.length) ? 'a' : 'div',
40616             cls: cls,
40617             cn: [
40618                 {
40619                     tag: 'div',
40620                     cls: 'masonry-brick-mask'
40621                 },
40622                 {
40623                     tag: 'div',
40624                     cls: 'masonry-brick-paragraph',
40625                     cn: []
40626                 }
40627             ]
40628         };
40629         
40630         if(this.href.length){
40631             cfg.href = this.href;
40632         }
40633         
40634         var cn = cfg.cn[1].cn;
40635         
40636         if(this.title.length){
40637             cn.push({
40638                 tag: 'h4',
40639                 cls: 'masonry-brick-title',
40640                 html: this.title
40641             });
40642         }
40643         
40644         if(this.html.length){
40645             cn.push({
40646                 tag: 'p',
40647                 cls: 'masonry-brick-text',
40648                 html: this.html
40649             });
40650         }
40651         
40652         if (!this.title.length && !this.html.length) {
40653             cfg.cn[1].cls += ' hide';
40654         }
40655         
40656         if(this.bgimage.length){
40657             cfg.cn.push({
40658                 tag: 'img',
40659                 cls: 'masonry-brick-image-view',
40660                 src: this.bgimage
40661             });
40662         }
40663         
40664         if(this.videourl.length){
40665             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40666             // youtube support only?
40667             cfg.cn.push({
40668                 tag: 'iframe',
40669                 cls: 'masonry-brick-image-view',
40670                 src: vurl,
40671                 frameborder : 0,
40672                 allowfullscreen : true
40673             });
40674         }
40675         
40676         return cfg;
40677         
40678     },
40679     
40680     getSplitAutoCreate : function()
40681     {
40682         var cls = 'masonry-brick masonry-brick-split';
40683         
40684         if(this.href.length){
40685             cls += ' masonry-brick-link';
40686         }
40687         
40688         if(this.bgimage.length){
40689             cls += ' masonry-brick-image';
40690         }
40691         
40692         if(this.size){
40693             cls += ' masonry-' + this.size + '-brick';
40694         }
40695         
40696         switch (this.placetitle) {
40697             case 'center' :
40698                 cls += ' masonry-center-title';
40699                 break;
40700             case 'bottom' :
40701                 cls += ' masonry-bottom-title';
40702                 break;
40703             default:
40704                 if(!this.bgimage.length){
40705                     cls += ' masonry-center-title';
40706                 }
40707
40708                 if(this.bgimage.length){
40709                     cls += ' masonry-bottom-title';
40710                 }
40711                 break;
40712         }
40713         
40714         if(this.cls){
40715             cls += ' ' + this.cls;
40716         }
40717         
40718         var cfg = {
40719             tag: (this.href.length) ? 'a' : 'div',
40720             cls: cls,
40721             cn: [
40722                 {
40723                     tag: 'div',
40724                     cls: 'masonry-brick-split-head',
40725                     cn: [
40726                         {
40727                             tag: 'div',
40728                             cls: 'masonry-brick-paragraph',
40729                             cn: []
40730                         }
40731                     ]
40732                 },
40733                 {
40734                     tag: 'div',
40735                     cls: 'masonry-brick-split-body',
40736                     cn: []
40737                 }
40738             ]
40739         };
40740         
40741         if(this.href.length){
40742             cfg.href = this.href;
40743         }
40744         
40745         if(this.title.length){
40746             cfg.cn[0].cn[0].cn.push({
40747                 tag: 'h4',
40748                 cls: 'masonry-brick-title',
40749                 html: this.title
40750             });
40751         }
40752         
40753         if(this.html.length){
40754             cfg.cn[1].cn.push({
40755                 tag: 'p',
40756                 cls: 'masonry-brick-text',
40757                 html: this.html
40758             });
40759         }
40760
40761         if(this.bgimage.length){
40762             cfg.cn[0].cn.push({
40763                 tag: 'img',
40764                 cls: 'masonry-brick-image-view',
40765                 src: this.bgimage
40766             });
40767         }
40768         
40769         if(this.videourl.length){
40770             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40771             // youtube support only?
40772             cfg.cn[0].cn.cn.push({
40773                 tag: 'iframe',
40774                 cls: 'masonry-brick-image-view',
40775                 src: vurl,
40776                 frameborder : 0,
40777                 allowfullscreen : true
40778             });
40779         }
40780         
40781         return cfg;
40782     },
40783     
40784     initEvents: function() 
40785     {
40786         switch (this.size) {
40787             case 'xs' :
40788                 this.x = 1;
40789                 this.y = 1;
40790                 break;
40791             case 'sm' :
40792                 this.x = 2;
40793                 this.y = 2;
40794                 break;
40795             case 'md' :
40796             case 'md-left' :
40797             case 'md-right' :
40798                 this.x = 3;
40799                 this.y = 3;
40800                 break;
40801             case 'tall' :
40802                 this.x = 2;
40803                 this.y = 3;
40804                 break;
40805             case 'wide' :
40806                 this.x = 3;
40807                 this.y = 2;
40808                 break;
40809             case 'wide-thin' :
40810                 this.x = 3;
40811                 this.y = 1;
40812                 break;
40813                         
40814             default :
40815                 break;
40816         }
40817         
40818         if(Roo.isTouch){
40819             this.el.on('touchstart', this.onTouchStart, this);
40820             this.el.on('touchmove', this.onTouchMove, this);
40821             this.el.on('touchend', this.onTouchEnd, this);
40822             this.el.on('contextmenu', this.onContextMenu, this);
40823         } else {
40824             this.el.on('mouseenter'  ,this.enter, this);
40825             this.el.on('mouseleave', this.leave, this);
40826             this.el.on('click', this.onClick, this);
40827         }
40828         
40829         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40830             this.parent().bricks.push(this);   
40831         }
40832         
40833     },
40834     
40835     onClick: function(e, el)
40836     {
40837         var time = this.endTimer - this.startTimer;
40838         // Roo.log(e.preventDefault());
40839         if(Roo.isTouch){
40840             if(time > 1000){
40841                 e.preventDefault();
40842                 return;
40843             }
40844         }
40845         
40846         if(!this.preventDefault){
40847             return;
40848         }
40849         
40850         e.preventDefault();
40851         
40852         if (this.activeClass != '') {
40853             this.selectBrick();
40854         }
40855         
40856         this.fireEvent('click', this, e);
40857     },
40858     
40859     enter: function(e, el)
40860     {
40861         e.preventDefault();
40862         
40863         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40864             return;
40865         }
40866         
40867         if(this.bgimage.length && this.html.length){
40868             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40869         }
40870     },
40871     
40872     leave: function(e, el)
40873     {
40874         e.preventDefault();
40875         
40876         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40877             return;
40878         }
40879         
40880         if(this.bgimage.length && this.html.length){
40881             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40882         }
40883     },
40884     
40885     onTouchStart: function(e, el)
40886     {
40887 //        e.preventDefault();
40888         
40889         this.touchmoved = false;
40890         
40891         if(!this.isFitContainer){
40892             return;
40893         }
40894         
40895         if(!this.bgimage.length || !this.html.length){
40896             return;
40897         }
40898         
40899         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40900         
40901         this.timer = new Date().getTime();
40902         
40903     },
40904     
40905     onTouchMove: function(e, el)
40906     {
40907         this.touchmoved = true;
40908     },
40909     
40910     onContextMenu : function(e,el)
40911     {
40912         e.preventDefault();
40913         e.stopPropagation();
40914         return false;
40915     },
40916     
40917     onTouchEnd: function(e, el)
40918     {
40919 //        e.preventDefault();
40920         
40921         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40922         
40923             this.leave(e,el);
40924             
40925             return;
40926         }
40927         
40928         if(!this.bgimage.length || !this.html.length){
40929             
40930             if(this.href.length){
40931                 window.location.href = this.href;
40932             }
40933             
40934             return;
40935         }
40936         
40937         if(!this.isFitContainer){
40938             return;
40939         }
40940         
40941         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40942         
40943         window.location.href = this.href;
40944     },
40945     
40946     //selection on single brick only
40947     selectBrick : function() {
40948         
40949         if (!this.parentId) {
40950             return;
40951         }
40952         
40953         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40954         var index = m.selectedBrick.indexOf(this.id);
40955         
40956         if ( index > -1) {
40957             m.selectedBrick.splice(index,1);
40958             this.el.removeClass(this.activeClass);
40959             return;
40960         }
40961         
40962         for(var i = 0; i < m.selectedBrick.length; i++) {
40963             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40964             b.el.removeClass(b.activeClass);
40965         }
40966         
40967         m.selectedBrick = [];
40968         
40969         m.selectedBrick.push(this.id);
40970         this.el.addClass(this.activeClass);
40971         return;
40972     },
40973     
40974     isSelected : function(){
40975         return this.el.hasClass(this.activeClass);
40976         
40977     }
40978 });
40979
40980 Roo.apply(Roo.bootstrap.MasonryBrick, {
40981     
40982     //groups: {},
40983     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40984      /**
40985     * register a Masonry Brick
40986     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40987     */
40988     
40989     register : function(brick)
40990     {
40991         //this.groups[brick.id] = brick;
40992         this.groups.add(brick.id, brick);
40993     },
40994     /**
40995     * fetch a  masonry brick based on the masonry brick ID
40996     * @param {string} the masonry brick to add
40997     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40998     */
40999     
41000     get: function(brick_id) 
41001     {
41002         // if (typeof(this.groups[brick_id]) == 'undefined') {
41003         //     return false;
41004         // }
41005         // return this.groups[brick_id] ;
41006         
41007         if(this.groups.key(brick_id)) {
41008             return this.groups.key(brick_id);
41009         }
41010         
41011         return false;
41012     }
41013     
41014     
41015     
41016 });
41017
41018  /*
41019  * - LGPL
41020  *
41021  * element
41022  * 
41023  */
41024
41025 /**
41026  * @class Roo.bootstrap.Brick
41027  * @extends Roo.bootstrap.Component
41028  * Bootstrap Brick class
41029  * 
41030  * @constructor
41031  * Create a new Brick
41032  * @param {Object} config The config object
41033  */
41034
41035 Roo.bootstrap.Brick = function(config){
41036     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41037     
41038     this.addEvents({
41039         // raw events
41040         /**
41041          * @event click
41042          * When a Brick is click
41043          * @param {Roo.bootstrap.Brick} this
41044          * @param {Roo.EventObject} e
41045          */
41046         "click" : true
41047     });
41048 };
41049
41050 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41051     
41052     /**
41053      * @cfg {String} title
41054      */   
41055     title : '',
41056     /**
41057      * @cfg {String} html
41058      */   
41059     html : '',
41060     /**
41061      * @cfg {String} bgimage
41062      */   
41063     bgimage : '',
41064     /**
41065      * @cfg {String} cls
41066      */   
41067     cls : '',
41068     /**
41069      * @cfg {String} href
41070      */   
41071     href : '',
41072     /**
41073      * @cfg {String} video
41074      */   
41075     video : '',
41076     /**
41077      * @cfg {Boolean} square
41078      */   
41079     square : true,
41080     
41081     getAutoCreate : function()
41082     {
41083         var cls = 'roo-brick';
41084         
41085         if(this.href.length){
41086             cls += ' roo-brick-link';
41087         }
41088         
41089         if(this.bgimage.length){
41090             cls += ' roo-brick-image';
41091         }
41092         
41093         if(!this.html.length && !this.bgimage.length){
41094             cls += ' roo-brick-center-title';
41095         }
41096         
41097         if(!this.html.length && this.bgimage.length){
41098             cls += ' roo-brick-bottom-title';
41099         }
41100         
41101         if(this.cls){
41102             cls += ' ' + this.cls;
41103         }
41104         
41105         var cfg = {
41106             tag: (this.href.length) ? 'a' : 'div',
41107             cls: cls,
41108             cn: [
41109                 {
41110                     tag: 'div',
41111                     cls: 'roo-brick-paragraph',
41112                     cn: []
41113                 }
41114             ]
41115         };
41116         
41117         if(this.href.length){
41118             cfg.href = this.href;
41119         }
41120         
41121         var cn = cfg.cn[0].cn;
41122         
41123         if(this.title.length){
41124             cn.push({
41125                 tag: 'h4',
41126                 cls: 'roo-brick-title',
41127                 html: this.title
41128             });
41129         }
41130         
41131         if(this.html.length){
41132             cn.push({
41133                 tag: 'p',
41134                 cls: 'roo-brick-text',
41135                 html: this.html
41136             });
41137         } else {
41138             cn.cls += ' hide';
41139         }
41140         
41141         if(this.bgimage.length){
41142             cfg.cn.push({
41143                 tag: 'img',
41144                 cls: 'roo-brick-image-view',
41145                 src: this.bgimage
41146             });
41147         }
41148         
41149         return cfg;
41150     },
41151     
41152     initEvents: function() 
41153     {
41154         if(this.title.length || this.html.length){
41155             this.el.on('mouseenter'  ,this.enter, this);
41156             this.el.on('mouseleave', this.leave, this);
41157         }
41158         
41159         Roo.EventManager.onWindowResize(this.resize, this); 
41160         
41161         if(this.bgimage.length){
41162             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41163             this.imageEl.on('load', this.onImageLoad, this);
41164             return;
41165         }
41166         
41167         this.resize();
41168     },
41169     
41170     onImageLoad : function()
41171     {
41172         this.resize();
41173     },
41174     
41175     resize : function()
41176     {
41177         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41178         
41179         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41180         
41181         if(this.bgimage.length){
41182             var image = this.el.select('.roo-brick-image-view', true).first();
41183             
41184             image.setWidth(paragraph.getWidth());
41185             
41186             if(this.square){
41187                 image.setHeight(paragraph.getWidth());
41188             }
41189             
41190             this.el.setHeight(image.getHeight());
41191             paragraph.setHeight(image.getHeight());
41192             
41193         }
41194         
41195     },
41196     
41197     enter: function(e, el)
41198     {
41199         e.preventDefault();
41200         
41201         if(this.bgimage.length){
41202             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41203             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41204         }
41205     },
41206     
41207     leave: function(e, el)
41208     {
41209         e.preventDefault();
41210         
41211         if(this.bgimage.length){
41212             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41213             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41214         }
41215     }
41216     
41217 });
41218
41219  
41220
41221  /*
41222  * - LGPL
41223  *
41224  * Number field 
41225  */
41226
41227 /**
41228  * @class Roo.bootstrap.form.NumberField
41229  * @extends Roo.bootstrap.form.Input
41230  * Bootstrap NumberField class
41231  * 
41232  * 
41233  * 
41234  * 
41235  * @constructor
41236  * Create a new NumberField
41237  * @param {Object} config The config object
41238  */
41239
41240 Roo.bootstrap.form.NumberField = function(config){
41241     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41242 };
41243
41244 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41245     
41246     /**
41247      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41248      */
41249     allowDecimals : true,
41250     /**
41251      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41252      */
41253     decimalSeparator : ".",
41254     /**
41255      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41256      */
41257     decimalPrecision : 2,
41258     /**
41259      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41260      */
41261     allowNegative : true,
41262     
41263     /**
41264      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41265      */
41266     allowZero: true,
41267     /**
41268      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41269      */
41270     minValue : Number.NEGATIVE_INFINITY,
41271     /**
41272      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41273      */
41274     maxValue : Number.MAX_VALUE,
41275     /**
41276      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41277      */
41278     minText : "The minimum value for this field is {0}",
41279     /**
41280      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41281      */
41282     maxText : "The maximum value for this field is {0}",
41283     /**
41284      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41285      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41286      */
41287     nanText : "{0} is not a valid number",
41288     /**
41289      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41290      */
41291     thousandsDelimiter : false,
41292     /**
41293      * @cfg {String} valueAlign alignment of value
41294      */
41295     valueAlign : "left",
41296
41297     getAutoCreate : function()
41298     {
41299         var hiddenInput = {
41300             tag: 'input',
41301             type: 'hidden',
41302             id: Roo.id(),
41303             cls: 'hidden-number-input'
41304         };
41305         
41306         if (this.name) {
41307             hiddenInput.name = this.name;
41308         }
41309         
41310         this.name = '';
41311         
41312         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41313         
41314         this.name = hiddenInput.name;
41315         
41316         if(cfg.cn.length > 0) {
41317             cfg.cn.push(hiddenInput);
41318         }
41319         
41320         return cfg;
41321     },
41322
41323     // private
41324     initEvents : function()
41325     {   
41326         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41327         
41328         var allowed = "0123456789";
41329         
41330         if(this.allowDecimals){
41331             allowed += this.decimalSeparator;
41332         }
41333         
41334         if(this.allowNegative){
41335             allowed += "-";
41336         }
41337         
41338         if(this.thousandsDelimiter) {
41339             allowed += ",";
41340         }
41341         
41342         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41343         
41344         var keyPress = function(e){
41345             
41346             var k = e.getKey();
41347             
41348             var c = e.getCharCode();
41349             
41350             if(
41351                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41352                     allowed.indexOf(String.fromCharCode(c)) === -1
41353             ){
41354                 e.stopEvent();
41355                 return;
41356             }
41357             
41358             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41359                 return;
41360             }
41361             
41362             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41363                 e.stopEvent();
41364             }
41365         };
41366         
41367         this.el.on("keypress", keyPress, this);
41368     },
41369     
41370     validateValue : function(value)
41371     {
41372         
41373         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41374             return false;
41375         }
41376         
41377         var num = this.parseValue(value);
41378         
41379         if(isNaN(num)){
41380             this.markInvalid(String.format(this.nanText, value));
41381             return false;
41382         }
41383         
41384         if(num < this.minValue){
41385             this.markInvalid(String.format(this.minText, this.minValue));
41386             return false;
41387         }
41388         
41389         if(num > this.maxValue){
41390             this.markInvalid(String.format(this.maxText, this.maxValue));
41391             return false;
41392         }
41393         
41394         return true;
41395     },
41396
41397     getValue : function()
41398     {
41399         var v = this.hiddenEl().getValue();
41400         
41401         return this.fixPrecision(this.parseValue(v));
41402     },
41403
41404     parseValue : function(value)
41405     {
41406         if(this.thousandsDelimiter) {
41407             value += "";
41408             r = new RegExp(",", "g");
41409             value = value.replace(r, "");
41410         }
41411         
41412         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41413         return isNaN(value) ? '' : value;
41414     },
41415
41416     fixPrecision : function(value)
41417     {
41418         if(this.thousandsDelimiter) {
41419             value += "";
41420             r = new RegExp(",", "g");
41421             value = value.replace(r, "");
41422         }
41423         
41424         var nan = isNaN(value);
41425         
41426         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41427             return nan ? '' : value;
41428         }
41429         return parseFloat(value).toFixed(this.decimalPrecision);
41430     },
41431
41432     setValue : function(v)
41433     {
41434         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41435         
41436         this.value = v;
41437         
41438         if(this.rendered){
41439             
41440             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41441             
41442             this.inputEl().dom.value = (v == '') ? '' :
41443                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41444             
41445             if(!this.allowZero && v === '0') {
41446                 this.hiddenEl().dom.value = '';
41447                 this.inputEl().dom.value = '';
41448             }
41449             
41450             this.validate();
41451         }
41452     },
41453
41454     decimalPrecisionFcn : function(v)
41455     {
41456         return Math.floor(v);
41457     },
41458
41459     beforeBlur : function()
41460     {
41461         var v = this.parseValue(this.getRawValue());
41462         
41463         if(v || v === 0 || v === ''){
41464             this.setValue(v);
41465         }
41466     },
41467     
41468     hiddenEl : function()
41469     {
41470         return this.el.select('input.hidden-number-input',true).first();
41471     }
41472     
41473 });
41474
41475  
41476
41477 /*
41478 * Licence: LGPL
41479 */
41480
41481 /**
41482  * @class Roo.bootstrap.DocumentSlider
41483  * @extends Roo.bootstrap.Component
41484  * Bootstrap DocumentSlider class
41485  * 
41486  * @constructor
41487  * Create a new DocumentViewer
41488  * @param {Object} config The config object
41489  */
41490
41491 Roo.bootstrap.DocumentSlider = function(config){
41492     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41493     
41494     this.files = [];
41495     
41496     this.addEvents({
41497         /**
41498          * @event initial
41499          * Fire after initEvent
41500          * @param {Roo.bootstrap.DocumentSlider} this
41501          */
41502         "initial" : true,
41503         /**
41504          * @event update
41505          * Fire after update
41506          * @param {Roo.bootstrap.DocumentSlider} this
41507          */
41508         "update" : true,
41509         /**
41510          * @event click
41511          * Fire after click
41512          * @param {Roo.bootstrap.DocumentSlider} this
41513          */
41514         "click" : true
41515     });
41516 };
41517
41518 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41519     
41520     files : false,
41521     
41522     indicator : 0,
41523     
41524     getAutoCreate : function()
41525     {
41526         var cfg = {
41527             tag : 'div',
41528             cls : 'roo-document-slider',
41529             cn : [
41530                 {
41531                     tag : 'div',
41532                     cls : 'roo-document-slider-header',
41533                     cn : [
41534                         {
41535                             tag : 'div',
41536                             cls : 'roo-document-slider-header-title'
41537                         }
41538                     ]
41539                 },
41540                 {
41541                     tag : 'div',
41542                     cls : 'roo-document-slider-body',
41543                     cn : [
41544                         {
41545                             tag : 'div',
41546                             cls : 'roo-document-slider-prev',
41547                             cn : [
41548                                 {
41549                                     tag : 'i',
41550                                     cls : 'fa fa-chevron-left'
41551                                 }
41552                             ]
41553                         },
41554                         {
41555                             tag : 'div',
41556                             cls : 'roo-document-slider-thumb',
41557                             cn : [
41558                                 {
41559                                     tag : 'img',
41560                                     cls : 'roo-document-slider-image'
41561                                 }
41562                             ]
41563                         },
41564                         {
41565                             tag : 'div',
41566                             cls : 'roo-document-slider-next',
41567                             cn : [
41568                                 {
41569                                     tag : 'i',
41570                                     cls : 'fa fa-chevron-right'
41571                                 }
41572                             ]
41573                         }
41574                     ]
41575                 }
41576             ]
41577         };
41578         
41579         return cfg;
41580     },
41581     
41582     initEvents : function()
41583     {
41584         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41585         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41586         
41587         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41588         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41589         
41590         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41591         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41592         
41593         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41594         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41595         
41596         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41597         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41598         
41599         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41600         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41601         
41602         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41603         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41604         
41605         this.thumbEl.on('click', this.onClick, this);
41606         
41607         this.prevIndicator.on('click', this.prev, this);
41608         
41609         this.nextIndicator.on('click', this.next, this);
41610         
41611     },
41612     
41613     initial : function()
41614     {
41615         if(this.files.length){
41616             this.indicator = 1;
41617             this.update()
41618         }
41619         
41620         this.fireEvent('initial', this);
41621     },
41622     
41623     update : function()
41624     {
41625         this.imageEl.attr('src', this.files[this.indicator - 1]);
41626         
41627         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41628         
41629         this.prevIndicator.show();
41630         
41631         if(this.indicator == 1){
41632             this.prevIndicator.hide();
41633         }
41634         
41635         this.nextIndicator.show();
41636         
41637         if(this.indicator == this.files.length){
41638             this.nextIndicator.hide();
41639         }
41640         
41641         this.thumbEl.scrollTo('top');
41642         
41643         this.fireEvent('update', this);
41644     },
41645     
41646     onClick : function(e)
41647     {
41648         e.preventDefault();
41649         
41650         this.fireEvent('click', this);
41651     },
41652     
41653     prev : function(e)
41654     {
41655         e.preventDefault();
41656         
41657         this.indicator = Math.max(1, this.indicator - 1);
41658         
41659         this.update();
41660     },
41661     
41662     next : function(e)
41663     {
41664         e.preventDefault();
41665         
41666         this.indicator = Math.min(this.files.length, this.indicator + 1);
41667         
41668         this.update();
41669     }
41670 });
41671 /*
41672  * - LGPL
41673  *
41674  * RadioSet
41675  *
41676  *
41677  */
41678
41679 /**
41680  * @class Roo.bootstrap.form.RadioSet
41681  * @extends Roo.bootstrap.form.Input
41682  * @children Roo.bootstrap.form.Radio
41683  * Bootstrap RadioSet class
41684  * @cfg {String} indicatorpos (left|right) default left
41685  * @cfg {Boolean} inline (true|false) inline the element (default true)
41686  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41687  * @constructor
41688  * Create a new RadioSet
41689  * @param {Object} config The config object
41690  */
41691
41692 Roo.bootstrap.form.RadioSet = function(config){
41693     
41694     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41695     
41696     this.radioes = [];
41697     
41698     Roo.bootstrap.form.RadioSet.register(this);
41699     
41700     this.addEvents({
41701         /**
41702         * @event check
41703         * Fires when the element is checked or unchecked.
41704         * @param {Roo.bootstrap.form.RadioSet} this This radio
41705         * @param {Roo.bootstrap.form.Radio} item The checked item
41706         */
41707        check : true,
41708        /**
41709         * @event click
41710         * Fires when the element is click.
41711         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41712         * @param {Roo.bootstrap.form.Radio} item The checked item
41713         * @param {Roo.EventObject} e The event object
41714         */
41715        click : true
41716     });
41717     
41718 };
41719
41720 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41721
41722     radioes : false,
41723     
41724     inline : true,
41725     
41726     weight : '',
41727     
41728     indicatorpos : 'left',
41729     
41730     getAutoCreate : function()
41731     {
41732         var label = {
41733             tag : 'label',
41734             cls : 'roo-radio-set-label',
41735             cn : [
41736                 {
41737                     tag : 'span',
41738                     html : this.fieldLabel
41739                 }
41740             ]
41741         };
41742         if (Roo.bootstrap.version == 3) {
41743             
41744             
41745             if(this.indicatorpos == 'left'){
41746                 label.cn.unshift({
41747                     tag : 'i',
41748                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41749                     tooltip : 'This field is required'
41750                 });
41751             } else {
41752                 label.cn.push({
41753                     tag : 'i',
41754                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41755                     tooltip : 'This field is required'
41756                 });
41757             }
41758         }
41759         var items = {
41760             tag : 'div',
41761             cls : 'roo-radio-set-items'
41762         };
41763         
41764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41765         
41766         if (align === 'left' && this.fieldLabel.length) {
41767             
41768             items = {
41769                 cls : "roo-radio-set-right", 
41770                 cn: [
41771                     items
41772                 ]
41773             };
41774             
41775             if(this.labelWidth > 12){
41776                 label.style = "width: " + this.labelWidth + 'px';
41777             }
41778             
41779             if(this.labelWidth < 13 && this.labelmd == 0){
41780                 this.labelmd = this.labelWidth;
41781             }
41782             
41783             if(this.labellg > 0){
41784                 label.cls += ' col-lg-' + this.labellg;
41785                 items.cls += ' col-lg-' + (12 - this.labellg);
41786             }
41787             
41788             if(this.labelmd > 0){
41789                 label.cls += ' col-md-' + this.labelmd;
41790                 items.cls += ' col-md-' + (12 - this.labelmd);
41791             }
41792             
41793             if(this.labelsm > 0){
41794                 label.cls += ' col-sm-' + this.labelsm;
41795                 items.cls += ' col-sm-' + (12 - this.labelsm);
41796             }
41797             
41798             if(this.labelxs > 0){
41799                 label.cls += ' col-xs-' + this.labelxs;
41800                 items.cls += ' col-xs-' + (12 - this.labelxs);
41801             }
41802         }
41803         
41804         var cfg = {
41805             tag : 'div',
41806             cls : 'roo-radio-set',
41807             cn : [
41808                 {
41809                     tag : 'input',
41810                     cls : 'roo-radio-set-input',
41811                     type : 'hidden',
41812                     name : this.name,
41813                     value : this.value ? this.value :  ''
41814                 },
41815                 label,
41816                 items
41817             ]
41818         };
41819         
41820         if(this.weight.length){
41821             cfg.cls += ' roo-radio-' + this.weight;
41822         }
41823         
41824         if(this.inline) {
41825             cfg.cls += ' roo-radio-set-inline';
41826         }
41827         
41828         var settings=this;
41829         ['xs','sm','md','lg'].map(function(size){
41830             if (settings[size]) {
41831                 cfg.cls += ' col-' + size + '-' + settings[size];
41832             }
41833         });
41834         
41835         return cfg;
41836         
41837     },
41838
41839     initEvents : function()
41840     {
41841         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41842         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41843         
41844         if(!this.fieldLabel.length){
41845             this.labelEl.hide();
41846         }
41847         
41848         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41849         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41850         
41851         this.indicator = this.indicatorEl();
41852         
41853         if(this.indicator){
41854             this.indicator.addClass('invisible');
41855         }
41856         
41857         this.originalValue = this.getValue();
41858         
41859     },
41860     
41861     inputEl: function ()
41862     {
41863         return this.el.select('.roo-radio-set-input', true).first();
41864     },
41865     
41866     getChildContainer : function()
41867     {
41868         return this.itemsEl;
41869     },
41870     
41871     register : function(item)
41872     {
41873         this.radioes.push(item);
41874         
41875     },
41876     
41877     validate : function()
41878     {   
41879         if(this.getVisibilityEl().hasClass('hidden')){
41880             return true;
41881         }
41882         
41883         var valid = false;
41884         
41885         Roo.each(this.radioes, function(i){
41886             if(!i.checked){
41887                 return;
41888             }
41889             
41890             valid = true;
41891             return false;
41892         });
41893         
41894         if(this.allowBlank) {
41895             return true;
41896         }
41897         
41898         if(this.disabled || valid){
41899             this.markValid();
41900             return true;
41901         }
41902         
41903         this.markInvalid();
41904         return false;
41905         
41906     },
41907     
41908     markValid : function()
41909     {
41910         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41911             this.indicatorEl().removeClass('visible');
41912             this.indicatorEl().addClass('invisible');
41913         }
41914         
41915         
41916         if (Roo.bootstrap.version == 3) {
41917             this.el.removeClass([this.invalidClass, this.validClass]);
41918             this.el.addClass(this.validClass);
41919         } else {
41920             this.el.removeClass(['is-invalid','is-valid']);
41921             this.el.addClass(['is-valid']);
41922         }
41923         this.fireEvent('valid', this);
41924     },
41925     
41926     markInvalid : function(msg)
41927     {
41928         if(this.allowBlank || this.disabled){
41929             return;
41930         }
41931         
41932         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41933             this.indicatorEl().removeClass('invisible');
41934             this.indicatorEl().addClass('visible');
41935         }
41936         if (Roo.bootstrap.version == 3) {
41937             this.el.removeClass([this.invalidClass, this.validClass]);
41938             this.el.addClass(this.invalidClass);
41939         } else {
41940             this.el.removeClass(['is-invalid','is-valid']);
41941             this.el.addClass(['is-invalid']);
41942         }
41943         
41944         this.fireEvent('invalid', this, msg);
41945         
41946     },
41947     
41948     setValue : function(v, suppressEvent)
41949     {   
41950         if(this.value === v){
41951             return;
41952         }
41953         
41954         this.value = v;
41955         
41956         if(this.rendered){
41957             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41958         }
41959         
41960         Roo.each(this.radioes, function(i){
41961             i.checked = false;
41962             i.el.removeClass('checked');
41963         });
41964         
41965         Roo.each(this.radioes, function(i){
41966             
41967             if(i.value === v || i.value.toString() === v.toString()){
41968                 i.checked = true;
41969                 i.el.addClass('checked');
41970                 
41971                 if(suppressEvent !== true){
41972                     this.fireEvent('check', this, i);
41973                 }
41974                 
41975                 return false;
41976             }
41977             
41978         }, this);
41979         
41980         this.validate();
41981     },
41982     
41983     clearInvalid : function(){
41984         
41985         if(!this.el || this.preventMark){
41986             return;
41987         }
41988         
41989         this.el.removeClass([this.invalidClass]);
41990         
41991         this.fireEvent('valid', this);
41992     }
41993     
41994 });
41995
41996 Roo.apply(Roo.bootstrap.form.RadioSet, {
41997     
41998     groups: {},
41999     
42000     register : function(set)
42001     {
42002         this.groups[set.name] = set;
42003     },
42004     
42005     get: function(name) 
42006     {
42007         if (typeof(this.groups[name]) == 'undefined') {
42008             return false;
42009         }
42010         
42011         return this.groups[name] ;
42012     }
42013     
42014 });
42015 /*
42016  * Based on:
42017  * Ext JS Library 1.1.1
42018  * Copyright(c) 2006-2007, Ext JS, LLC.
42019  *
42020  * Originally Released Under LGPL - original licence link has changed is not relivant.
42021  *
42022  * Fork - LGPL
42023  * <script type="text/javascript">
42024  */
42025
42026
42027 /**
42028  * @class Roo.bootstrap.SplitBar
42029  * @extends Roo.util.Observable
42030  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42031  * <br><br>
42032  * Usage:
42033  * <pre><code>
42034 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42035                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42036 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42037 split.minSize = 100;
42038 split.maxSize = 600;
42039 split.animate = true;
42040 split.on('moved', splitterMoved);
42041 </code></pre>
42042  * @constructor
42043  * Create a new SplitBar
42044  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42045  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42046  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42047  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42048                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42049                         position of the SplitBar).
42050  */
42051 Roo.bootstrap.SplitBar = function(cfg){
42052     
42053     /** @private */
42054     
42055     //{
42056     //  dragElement : elm
42057     //  resizingElement: el,
42058         // optional..
42059     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42060     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42061         // existingProxy ???
42062     //}
42063     
42064     this.el = Roo.get(cfg.dragElement, true);
42065     this.el.dom.unselectable = "on";
42066     /** @private */
42067     this.resizingEl = Roo.get(cfg.resizingElement, true);
42068
42069     /**
42070      * @private
42071      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42072      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42073      * @type Number
42074      */
42075     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42076     
42077     /**
42078      * The minimum size of the resizing element. (Defaults to 0)
42079      * @type Number
42080      */
42081     this.minSize = 0;
42082     
42083     /**
42084      * The maximum size of the resizing element. (Defaults to 2000)
42085      * @type Number
42086      */
42087     this.maxSize = 2000;
42088     
42089     /**
42090      * Whether to animate the transition to the new size
42091      * @type Boolean
42092      */
42093     this.animate = false;
42094     
42095     /**
42096      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42097      * @type Boolean
42098      */
42099     this.useShim = false;
42100     
42101     /** @private */
42102     this.shim = null;
42103     
42104     if(!cfg.existingProxy){
42105         /** @private */
42106         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42107     }else{
42108         this.proxy = Roo.get(cfg.existingProxy).dom;
42109     }
42110     /** @private */
42111     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42112     
42113     /** @private */
42114     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42115     
42116     /** @private */
42117     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42118     
42119     /** @private */
42120     this.dragSpecs = {};
42121     
42122     /**
42123      * @private The adapter to use to positon and resize elements
42124      */
42125     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42126     this.adapter.init(this);
42127     
42128     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42129         /** @private */
42130         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42131         this.el.addClass("roo-splitbar-h");
42132     }else{
42133         /** @private */
42134         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42135         this.el.addClass("roo-splitbar-v");
42136     }
42137     
42138     this.addEvents({
42139         /**
42140          * @event resize
42141          * Fires when the splitter is moved (alias for {@link #event-moved})
42142          * @param {Roo.bootstrap.SplitBar} this
42143          * @param {Number} newSize the new width or height
42144          */
42145         "resize" : true,
42146         /**
42147          * @event moved
42148          * Fires when the splitter is moved
42149          * @param {Roo.bootstrap.SplitBar} this
42150          * @param {Number} newSize the new width or height
42151          */
42152         "moved" : true,
42153         /**
42154          * @event beforeresize
42155          * Fires before the splitter is dragged
42156          * @param {Roo.bootstrap.SplitBar} this
42157          */
42158         "beforeresize" : true,
42159
42160         "beforeapply" : true
42161     });
42162
42163     Roo.util.Observable.call(this);
42164 };
42165
42166 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42167     onStartProxyDrag : function(x, y){
42168         this.fireEvent("beforeresize", this);
42169         if(!this.overlay){
42170             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42171             o.unselectable();
42172             o.enableDisplayMode("block");
42173             // all splitbars share the same overlay
42174             Roo.bootstrap.SplitBar.prototype.overlay = o;
42175         }
42176         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42177         this.overlay.show();
42178         Roo.get(this.proxy).setDisplayed("block");
42179         var size = this.adapter.getElementSize(this);
42180         this.activeMinSize = this.getMinimumSize();;
42181         this.activeMaxSize = this.getMaximumSize();;
42182         var c1 = size - this.activeMinSize;
42183         var c2 = Math.max(this.activeMaxSize - size, 0);
42184         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42185             this.dd.resetConstraints();
42186             this.dd.setXConstraint(
42187                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42188                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42189             );
42190             this.dd.setYConstraint(0, 0);
42191         }else{
42192             this.dd.resetConstraints();
42193             this.dd.setXConstraint(0, 0);
42194             this.dd.setYConstraint(
42195                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42196                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42197             );
42198          }
42199         this.dragSpecs.startSize = size;
42200         this.dragSpecs.startPoint = [x, y];
42201         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42202     },
42203     
42204     /** 
42205      * @private Called after the drag operation by the DDProxy
42206      */
42207     onEndProxyDrag : function(e){
42208         Roo.get(this.proxy).setDisplayed(false);
42209         var endPoint = Roo.lib.Event.getXY(e);
42210         if(this.overlay){
42211             this.overlay.hide();
42212         }
42213         var newSize;
42214         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42215             newSize = this.dragSpecs.startSize + 
42216                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42217                     endPoint[0] - this.dragSpecs.startPoint[0] :
42218                     this.dragSpecs.startPoint[0] - endPoint[0]
42219                 );
42220         }else{
42221             newSize = this.dragSpecs.startSize + 
42222                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42223                     endPoint[1] - this.dragSpecs.startPoint[1] :
42224                     this.dragSpecs.startPoint[1] - endPoint[1]
42225                 );
42226         }
42227         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42228         if(newSize != this.dragSpecs.startSize){
42229             if(this.fireEvent('beforeapply', this, newSize) !== false){
42230                 this.adapter.setElementSize(this, newSize);
42231                 this.fireEvent("moved", this, newSize);
42232                 this.fireEvent("resize", this, newSize);
42233             }
42234         }
42235     },
42236     
42237     /**
42238      * Get the adapter this SplitBar uses
42239      * @return The adapter object
42240      */
42241     getAdapter : function(){
42242         return this.adapter;
42243     },
42244     
42245     /**
42246      * Set the adapter this SplitBar uses
42247      * @param {Object} adapter A SplitBar adapter object
42248      */
42249     setAdapter : function(adapter){
42250         this.adapter = adapter;
42251         this.adapter.init(this);
42252     },
42253     
42254     /**
42255      * Gets the minimum size for the resizing element
42256      * @return {Number} The minimum size
42257      */
42258     getMinimumSize : function(){
42259         return this.minSize;
42260     },
42261     
42262     /**
42263      * Sets the minimum size for the resizing element
42264      * @param {Number} minSize The minimum size
42265      */
42266     setMinimumSize : function(minSize){
42267         this.minSize = minSize;
42268     },
42269     
42270     /**
42271      * Gets the maximum size for the resizing element
42272      * @return {Number} The maximum size
42273      */
42274     getMaximumSize : function(){
42275         return this.maxSize;
42276     },
42277     
42278     /**
42279      * Sets the maximum size for the resizing element
42280      * @param {Number} maxSize The maximum size
42281      */
42282     setMaximumSize : function(maxSize){
42283         this.maxSize = maxSize;
42284     },
42285     
42286     /**
42287      * Sets the initialize size for the resizing element
42288      * @param {Number} size The initial size
42289      */
42290     setCurrentSize : function(size){
42291         var oldAnimate = this.animate;
42292         this.animate = false;
42293         this.adapter.setElementSize(this, size);
42294         this.animate = oldAnimate;
42295     },
42296     
42297     /**
42298      * Destroy this splitbar. 
42299      * @param {Boolean} removeEl True to remove the element
42300      */
42301     destroy : function(removeEl){
42302         if(this.shim){
42303             this.shim.remove();
42304         }
42305         this.dd.unreg();
42306         this.proxy.parentNode.removeChild(this.proxy);
42307         if(removeEl){
42308             this.el.remove();
42309         }
42310     }
42311 });
42312
42313 /**
42314  * @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.
42315  */
42316 Roo.bootstrap.SplitBar.createProxy = function(dir){
42317     var proxy = new Roo.Element(document.createElement("div"));
42318     proxy.unselectable();
42319     var cls = 'roo-splitbar-proxy';
42320     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42321     document.body.appendChild(proxy.dom);
42322     return proxy.dom;
42323 };
42324
42325 /** 
42326  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42327  * Default Adapter. It assumes the splitter and resizing element are not positioned
42328  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42329  */
42330 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42331 };
42332
42333 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42334     // do nothing for now
42335     init : function(s){
42336     
42337     },
42338     /**
42339      * Called before drag operations to get the current size of the resizing element. 
42340      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42341      */
42342      getElementSize : function(s){
42343         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42344             return s.resizingEl.getWidth();
42345         }else{
42346             return s.resizingEl.getHeight();
42347         }
42348     },
42349     
42350     /**
42351      * Called after drag operations to set the size of the resizing element.
42352      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42353      * @param {Number} newSize The new size to set
42354      * @param {Function} onComplete A function to be invoked when resizing is complete
42355      */
42356     setElementSize : function(s, newSize, onComplete){
42357         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42358             if(!s.animate){
42359                 s.resizingEl.setWidth(newSize);
42360                 if(onComplete){
42361                     onComplete(s, newSize);
42362                 }
42363             }else{
42364                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42365             }
42366         }else{
42367             
42368             if(!s.animate){
42369                 s.resizingEl.setHeight(newSize);
42370                 if(onComplete){
42371                     onComplete(s, newSize);
42372                 }
42373             }else{
42374                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42375             }
42376         }
42377     }
42378 };
42379
42380 /** 
42381  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42382  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42383  * Adapter that  moves the splitter element to align with the resized sizing element. 
42384  * Used with an absolute positioned SplitBar.
42385  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42386  * document.body, make sure you assign an id to the body element.
42387  */
42388 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42389     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42390     this.container = Roo.get(container);
42391 };
42392
42393 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42394     init : function(s){
42395         this.basic.init(s);
42396     },
42397     
42398     getElementSize : function(s){
42399         return this.basic.getElementSize(s);
42400     },
42401     
42402     setElementSize : function(s, newSize, onComplete){
42403         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42404     },
42405     
42406     moveSplitter : function(s){
42407         var yes = Roo.bootstrap.SplitBar;
42408         switch(s.placement){
42409             case yes.LEFT:
42410                 s.el.setX(s.resizingEl.getRight());
42411                 break;
42412             case yes.RIGHT:
42413                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42414                 break;
42415             case yes.TOP:
42416                 s.el.setY(s.resizingEl.getBottom());
42417                 break;
42418             case yes.BOTTOM:
42419                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42420                 break;
42421         }
42422     }
42423 };
42424
42425 /**
42426  * Orientation constant - Create a vertical SplitBar
42427  * @static
42428  * @type Number
42429  */
42430 Roo.bootstrap.SplitBar.VERTICAL = 1;
42431
42432 /**
42433  * Orientation constant - Create a horizontal SplitBar
42434  * @static
42435  * @type Number
42436  */
42437 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42438
42439 /**
42440  * Placement constant - The resizing element is to the left of the splitter element
42441  * @static
42442  * @type Number
42443  */
42444 Roo.bootstrap.SplitBar.LEFT = 1;
42445
42446 /**
42447  * Placement constant - The resizing element is to the right of the splitter element
42448  * @static
42449  * @type Number
42450  */
42451 Roo.bootstrap.SplitBar.RIGHT = 2;
42452
42453 /**
42454  * Placement constant - The resizing element is positioned above the splitter element
42455  * @static
42456  * @type Number
42457  */
42458 Roo.bootstrap.SplitBar.TOP = 3;
42459
42460 /**
42461  * Placement constant - The resizing element is positioned under splitter element
42462  * @static
42463  * @type Number
42464  */
42465 Roo.bootstrap.SplitBar.BOTTOM = 4;
42466 /*
42467  * Based on:
42468  * Ext JS Library 1.1.1
42469  * Copyright(c) 2006-2007, Ext JS, LLC.
42470  *
42471  * Originally Released Under LGPL - original licence link has changed is not relivant.
42472  *
42473  * Fork - LGPL
42474  * <script type="text/javascript">
42475  */
42476
42477 /**
42478  * @class Roo.bootstrap.layout.Manager
42479  * @extends Roo.bootstrap.Component
42480  * @abstract
42481  * Base class for layout managers.
42482  */
42483 Roo.bootstrap.layout.Manager = function(config)
42484 {
42485     this.monitorWindowResize = true; // do this before we apply configuration.
42486     
42487     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42488
42489
42490
42491
42492
42493     /** false to disable window resize monitoring @type Boolean */
42494     
42495     this.regions = {};
42496     this.addEvents({
42497         /**
42498          * @event layout
42499          * Fires when a layout is performed.
42500          * @param {Roo.LayoutManager} this
42501          */
42502         "layout" : true,
42503         /**
42504          * @event regionresized
42505          * Fires when the user resizes a region.
42506          * @param {Roo.LayoutRegion} region The resized region
42507          * @param {Number} newSize The new size (width for east/west, height for north/south)
42508          */
42509         "regionresized" : true,
42510         /**
42511          * @event regioncollapsed
42512          * Fires when a region is collapsed.
42513          * @param {Roo.LayoutRegion} region The collapsed region
42514          */
42515         "regioncollapsed" : true,
42516         /**
42517          * @event regionexpanded
42518          * Fires when a region is expanded.
42519          * @param {Roo.LayoutRegion} region The expanded region
42520          */
42521         "regionexpanded" : true
42522     });
42523     this.updating = false;
42524
42525     if (config.el) {
42526         this.el = Roo.get(config.el);
42527         this.initEvents();
42528     }
42529
42530 };
42531
42532 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42533
42534
42535     regions : null,
42536
42537     monitorWindowResize : true,
42538
42539
42540     updating : false,
42541
42542
42543     onRender : function(ct, position)
42544     {
42545         if(!this.el){
42546             this.el = Roo.get(ct);
42547             this.initEvents();
42548         }
42549         //this.fireEvent('render',this);
42550     },
42551
42552
42553     initEvents: function()
42554     {
42555
42556
42557         // ie scrollbar fix
42558         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42559             document.body.scroll = "no";
42560         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42561             this.el.position('relative');
42562         }
42563         this.id = this.el.id;
42564         this.el.addClass("roo-layout-container");
42565         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42566         if(this.el.dom != document.body ) {
42567             this.el.on('resize', this.layout,this);
42568             this.el.on('show', this.layout,this);
42569         }
42570
42571     },
42572
42573     /**
42574      * Returns true if this layout is currently being updated
42575      * @return {Boolean}
42576      */
42577     isUpdating : function(){
42578         return this.updating;
42579     },
42580
42581     /**
42582      * Suspend the LayoutManager from doing auto-layouts while
42583      * making multiple add or remove calls
42584      */
42585     beginUpdate : function(){
42586         this.updating = true;
42587     },
42588
42589     /**
42590      * Restore auto-layouts and optionally disable the manager from performing a layout
42591      * @param {Boolean} noLayout true to disable a layout update
42592      */
42593     endUpdate : function(noLayout){
42594         this.updating = false;
42595         if(!noLayout){
42596             this.layout();
42597         }
42598     },
42599
42600     layout: function(){
42601         // abstract...
42602     },
42603
42604     onRegionResized : function(region, newSize){
42605         this.fireEvent("regionresized", region, newSize);
42606         this.layout();
42607     },
42608
42609     onRegionCollapsed : function(region){
42610         this.fireEvent("regioncollapsed", region);
42611     },
42612
42613     onRegionExpanded : function(region){
42614         this.fireEvent("regionexpanded", region);
42615     },
42616
42617     /**
42618      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42619      * performs box-model adjustments.
42620      * @return {Object} The size as an object {width: (the width), height: (the height)}
42621      */
42622     getViewSize : function()
42623     {
42624         var size;
42625         if(this.el.dom != document.body){
42626             size = this.el.getSize();
42627         }else{
42628             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42629         }
42630         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42631         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42632         return size;
42633     },
42634
42635     /**
42636      * Returns the Element this layout is bound to.
42637      * @return {Roo.Element}
42638      */
42639     getEl : function(){
42640         return this.el;
42641     },
42642
42643     /**
42644      * Returns the specified region.
42645      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42646      * @return {Roo.LayoutRegion}
42647      */
42648     getRegion : function(target){
42649         return this.regions[target.toLowerCase()];
42650     },
42651
42652     onWindowResize : function(){
42653         if(this.monitorWindowResize){
42654             this.layout();
42655         }
42656     }
42657 });
42658 /*
42659  * Based on:
42660  * Ext JS Library 1.1.1
42661  * Copyright(c) 2006-2007, Ext JS, LLC.
42662  *
42663  * Originally Released Under LGPL - original licence link has changed is not relivant.
42664  *
42665  * Fork - LGPL
42666  * <script type="text/javascript">
42667  */
42668 /**
42669  * @class Roo.bootstrap.layout.Border
42670  * @extends Roo.bootstrap.layout.Manager
42671  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42672  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42673  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42674  * please see: examples/bootstrap/nested.html<br><br>
42675  
42676 <b>The container the layout is rendered into can be either the body element or any other element.
42677 If it is not the body element, the container needs to either be an absolute positioned element,
42678 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42679 the container size if it is not the body element.</b>
42680
42681 * @constructor
42682 * Create a new Border
42683 * @param {Object} config Configuration options
42684  */
42685 Roo.bootstrap.layout.Border = function(config){
42686     config = config || {};
42687     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42688     
42689     
42690     
42691     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42692         if(config[region]){
42693             config[region].region = region;
42694             this.addRegion(config[region]);
42695         }
42696     },this);
42697     
42698 };
42699
42700 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42701
42702 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42703     
42704         /**
42705          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42706          */
42707         /**
42708          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42709          */
42710         /**
42711          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42712          */
42713         /**
42714          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42715          */
42716         /**
42717          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42718          */
42719         
42720         
42721         
42722         
42723     parent : false, // this might point to a 'nest' or a ???
42724     
42725     /**
42726      * Creates and adds a new region if it doesn't already exist.
42727      * @param {String} target The target region key (north, south, east, west or center).
42728      * @param {Object} config The regions config object
42729      * @return {BorderLayoutRegion} The new region
42730      */
42731     addRegion : function(config)
42732     {
42733         if(!this.regions[config.region]){
42734             var r = this.factory(config);
42735             this.bindRegion(r);
42736         }
42737         return this.regions[config.region];
42738     },
42739
42740     // private (kinda)
42741     bindRegion : function(r){
42742         this.regions[r.config.region] = r;
42743         
42744         r.on("visibilitychange",    this.layout, this);
42745         r.on("paneladded",          this.layout, this);
42746         r.on("panelremoved",        this.layout, this);
42747         r.on("invalidated",         this.layout, this);
42748         r.on("resized",             this.onRegionResized, this);
42749         r.on("collapsed",           this.onRegionCollapsed, this);
42750         r.on("expanded",            this.onRegionExpanded, this);
42751     },
42752
42753     /**
42754      * Performs a layout update.
42755      */
42756     layout : function()
42757     {
42758         if(this.updating) {
42759             return;
42760         }
42761         
42762         // render all the rebions if they have not been done alreayd?
42763         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42764             if(this.regions[region] && !this.regions[region].bodyEl){
42765                 this.regions[region].onRender(this.el)
42766             }
42767         },this);
42768         
42769         var size = this.getViewSize();
42770         var w = size.width;
42771         var h = size.height;
42772         var centerW = w;
42773         var centerH = h;
42774         var centerY = 0;
42775         var centerX = 0;
42776         //var x = 0, y = 0;
42777
42778         var rs = this.regions;
42779         var north = rs["north"];
42780         var south = rs["south"]; 
42781         var west = rs["west"];
42782         var east = rs["east"];
42783         var center = rs["center"];
42784         //if(this.hideOnLayout){ // not supported anymore
42785             //c.el.setStyle("display", "none");
42786         //}
42787         if(north && north.isVisible()){
42788             var b = north.getBox();
42789             var m = north.getMargins();
42790             b.width = w - (m.left+m.right);
42791             b.x = m.left;
42792             b.y = m.top;
42793             centerY = b.height + b.y + m.bottom;
42794             centerH -= centerY;
42795             north.updateBox(this.safeBox(b));
42796         }
42797         if(south && south.isVisible()){
42798             var b = south.getBox();
42799             var m = south.getMargins();
42800             b.width = w - (m.left+m.right);
42801             b.x = m.left;
42802             var totalHeight = (b.height + m.top + m.bottom);
42803             b.y = h - totalHeight + m.top;
42804             centerH -= totalHeight;
42805             south.updateBox(this.safeBox(b));
42806         }
42807         if(west && west.isVisible()){
42808             var b = west.getBox();
42809             var m = west.getMargins();
42810             b.height = centerH - (m.top+m.bottom);
42811             b.x = m.left;
42812             b.y = centerY + m.top;
42813             var totalWidth = (b.width + m.left + m.right);
42814             centerX += totalWidth;
42815             centerW -= totalWidth;
42816             west.updateBox(this.safeBox(b));
42817         }
42818         if(east && east.isVisible()){
42819             var b = east.getBox();
42820             var m = east.getMargins();
42821             b.height = centerH - (m.top+m.bottom);
42822             var totalWidth = (b.width + m.left + m.right);
42823             b.x = w - totalWidth + m.left;
42824             b.y = centerY + m.top;
42825             centerW -= totalWidth;
42826             east.updateBox(this.safeBox(b));
42827         }
42828         if(center){
42829             var m = center.getMargins();
42830             var centerBox = {
42831                 x: centerX + m.left,
42832                 y: centerY + m.top,
42833                 width: centerW - (m.left+m.right),
42834                 height: centerH - (m.top+m.bottom)
42835             };
42836             //if(this.hideOnLayout){
42837                 //center.el.setStyle("display", "block");
42838             //}
42839             center.updateBox(this.safeBox(centerBox));
42840         }
42841         this.el.repaint();
42842         this.fireEvent("layout", this);
42843     },
42844
42845     // private
42846     safeBox : function(box){
42847         box.width = Math.max(0, box.width);
42848         box.height = Math.max(0, box.height);
42849         return box;
42850     },
42851
42852     /**
42853      * Adds a ContentPanel (or subclass) to this layout.
42854      * @param {String} target The target region key (north, south, east, west or center).
42855      * @param {Roo.ContentPanel} panel The panel to add
42856      * @return {Roo.ContentPanel} The added panel
42857      */
42858     add : function(target, panel){
42859          
42860         target = target.toLowerCase();
42861         return this.regions[target].add(panel);
42862     },
42863
42864     /**
42865      * Remove a ContentPanel (or subclass) to this layout.
42866      * @param {String} target The target region key (north, south, east, west or center).
42867      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42868      * @return {Roo.ContentPanel} The removed panel
42869      */
42870     remove : function(target, panel){
42871         target = target.toLowerCase();
42872         return this.regions[target].remove(panel);
42873     },
42874
42875     /**
42876      * Searches all regions for a panel with the specified id
42877      * @param {String} panelId
42878      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42879      */
42880     findPanel : function(panelId){
42881         var rs = this.regions;
42882         for(var target in rs){
42883             if(typeof rs[target] != "function"){
42884                 var p = rs[target].getPanel(panelId);
42885                 if(p){
42886                     return p;
42887                 }
42888             }
42889         }
42890         return null;
42891     },
42892
42893     /**
42894      * Searches all regions for a panel with the specified id and activates (shows) it.
42895      * @param {String/ContentPanel} panelId The panels id or the panel itself
42896      * @return {Roo.ContentPanel} The shown panel or null
42897      */
42898     showPanel : function(panelId) {
42899       var rs = this.regions;
42900       for(var target in rs){
42901          var r = rs[target];
42902          if(typeof r != "function"){
42903             if(r.hasPanel(panelId)){
42904                return r.showPanel(panelId);
42905             }
42906          }
42907       }
42908       return null;
42909    },
42910
42911    /**
42912      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42913      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42914      */
42915    /*
42916     restoreState : function(provider){
42917         if(!provider){
42918             provider = Roo.state.Manager;
42919         }
42920         var sm = new Roo.LayoutStateManager();
42921         sm.init(this, provider);
42922     },
42923 */
42924  
42925  
42926     /**
42927      * Adds a xtype elements to the layout.
42928      * <pre><code>
42929
42930 layout.addxtype({
42931        xtype : 'ContentPanel',
42932        region: 'west',
42933        items: [ .... ]
42934    }
42935 );
42936
42937 layout.addxtype({
42938         xtype : 'NestedLayoutPanel',
42939         region: 'west',
42940         layout: {
42941            center: { },
42942            west: { }   
42943         },
42944         items : [ ... list of content panels or nested layout panels.. ]
42945    }
42946 );
42947 </code></pre>
42948      * @param {Object} cfg Xtype definition of item to add.
42949      */
42950     addxtype : function(cfg)
42951     {
42952         // basically accepts a pannel...
42953         // can accept a layout region..!?!?
42954         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42955         
42956         
42957         // theory?  children can only be panels??
42958         
42959         //if (!cfg.xtype.match(/Panel$/)) {
42960         //    return false;
42961         //}
42962         var ret = false;
42963         
42964         if (typeof(cfg.region) == 'undefined') {
42965             Roo.log("Failed to add Panel, region was not set");
42966             Roo.log(cfg);
42967             return false;
42968         }
42969         var region = cfg.region;
42970         delete cfg.region;
42971         
42972           
42973         var xitems = [];
42974         if (cfg.items) {
42975             xitems = cfg.items;
42976             delete cfg.items;
42977         }
42978         var nb = false;
42979         
42980         if ( region == 'center') {
42981             Roo.log("Center: " + cfg.title);
42982         }
42983         
42984         
42985         switch(cfg.xtype) 
42986         {
42987             case 'Content':  // ContentPanel (el, cfg)
42988             case 'Scroll':  // ContentPanel (el, cfg)
42989             case 'View': 
42990                 cfg.autoCreate = cfg.autoCreate || true;
42991                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42992                 //} else {
42993                 //    var el = this.el.createChild();
42994                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42995                 //}
42996                 
42997                 this.add(region, ret);
42998                 break;
42999             
43000             /*
43001             case 'TreePanel': // our new panel!
43002                 cfg.el = this.el.createChild();
43003                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43004                 this.add(region, ret);
43005                 break;
43006             */
43007             
43008             case 'Nest': 
43009                 // create a new Layout (which is  a Border Layout...
43010                 
43011                 var clayout = cfg.layout;
43012                 clayout.el  = this.el.createChild();
43013                 clayout.items   = clayout.items  || [];
43014                 
43015                 delete cfg.layout;
43016                 
43017                 // replace this exitems with the clayout ones..
43018                 xitems = clayout.items;
43019                  
43020                 // force background off if it's in center...
43021                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43022                     cfg.background = false;
43023                 }
43024                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43025                 
43026                 
43027                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43028                 //console.log('adding nested layout panel '  + cfg.toSource());
43029                 this.add(region, ret);
43030                 nb = {}; /// find first...
43031                 break;
43032             
43033             case 'Grid':
43034                 
43035                 // needs grid and region
43036                 
43037                 //var el = this.getRegion(region).el.createChild();
43038                 /*
43039                  *var el = this.el.createChild();
43040                 // create the grid first...
43041                 cfg.grid.container = el;
43042                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43043                 */
43044                 
43045                 if (region == 'center' && this.active ) {
43046                     cfg.background = false;
43047                 }
43048                 
43049                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43050                 
43051                 this.add(region, ret);
43052                 /*
43053                 if (cfg.background) {
43054                     // render grid on panel activation (if panel background)
43055                     ret.on('activate', function(gp) {
43056                         if (!gp.grid.rendered) {
43057                     //        gp.grid.render(el);
43058                         }
43059                     });
43060                 } else {
43061                   //  cfg.grid.render(el);
43062                 }
43063                 */
43064                 break;
43065            
43066            
43067             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43068                 // it was the old xcomponent building that caused this before.
43069                 // espeically if border is the top element in the tree.
43070                 ret = this;
43071                 break; 
43072                 
43073                     
43074                 
43075                 
43076                 
43077             default:
43078                 /*
43079                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43080                     
43081                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43082                     this.add(region, ret);
43083                 } else {
43084                 */
43085                     Roo.log(cfg);
43086                     throw "Can not add '" + cfg.xtype + "' to Border";
43087                     return null;
43088              
43089                                 
43090              
43091         }
43092         this.beginUpdate();
43093         // add children..
43094         var region = '';
43095         var abn = {};
43096         Roo.each(xitems, function(i)  {
43097             region = nb && i.region ? i.region : false;
43098             
43099             var add = ret.addxtype(i);
43100            
43101             if (region) {
43102                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43103                 if (!i.background) {
43104                     abn[region] = nb[region] ;
43105                 }
43106             }
43107             
43108         });
43109         this.endUpdate();
43110
43111         // make the last non-background panel active..
43112         //if (nb) { Roo.log(abn); }
43113         if (nb) {
43114             
43115             for(var r in abn) {
43116                 region = this.getRegion(r);
43117                 if (region) {
43118                     // tried using nb[r], but it does not work..
43119                      
43120                     region.showPanel(abn[r]);
43121                    
43122                 }
43123             }
43124         }
43125         return ret;
43126         
43127     },
43128     
43129     
43130 // private
43131     factory : function(cfg)
43132     {
43133         
43134         var validRegions = Roo.bootstrap.layout.Border.regions;
43135
43136         var target = cfg.region;
43137         cfg.mgr = this;
43138         
43139         var r = Roo.bootstrap.layout;
43140         Roo.log(target);
43141         switch(target){
43142             case "north":
43143                 return new r.North(cfg);
43144             case "south":
43145                 return new r.South(cfg);
43146             case "east":
43147                 return new r.East(cfg);
43148             case "west":
43149                 return new r.West(cfg);
43150             case "center":
43151                 return new r.Center(cfg);
43152         }
43153         throw 'Layout region "'+target+'" not supported.';
43154     }
43155     
43156     
43157 });
43158  /*
43159  * Based on:
43160  * Ext JS Library 1.1.1
43161  * Copyright(c) 2006-2007, Ext JS, LLC.
43162  *
43163  * Originally Released Under LGPL - original licence link has changed is not relivant.
43164  *
43165  * Fork - LGPL
43166  * <script type="text/javascript">
43167  */
43168  
43169 /**
43170  * @class Roo.bootstrap.layout.Basic
43171  * @extends Roo.util.Observable
43172  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43173  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43174  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43175  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43176  * @cfg {string}   region  the region that it inhabits..
43177  * @cfg {bool}   skipConfig skip config?
43178  * 
43179
43180  */
43181 Roo.bootstrap.layout.Basic = function(config){
43182     
43183     this.mgr = config.mgr;
43184     
43185     this.position = config.region;
43186     
43187     var skipConfig = config.skipConfig;
43188     
43189     this.events = {
43190         /**
43191          * @scope Roo.BasicLayoutRegion
43192          */
43193         
43194         /**
43195          * @event beforeremove
43196          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43197          * @param {Roo.LayoutRegion} this
43198          * @param {Roo.ContentPanel} panel The panel
43199          * @param {Object} e The cancel event object
43200          */
43201         "beforeremove" : true,
43202         /**
43203          * @event invalidated
43204          * Fires when the layout for this region is changed.
43205          * @param {Roo.LayoutRegion} this
43206          */
43207         "invalidated" : true,
43208         /**
43209          * @event visibilitychange
43210          * Fires when this region is shown or hidden 
43211          * @param {Roo.LayoutRegion} this
43212          * @param {Boolean} visibility true or false
43213          */
43214         "visibilitychange" : true,
43215         /**
43216          * @event paneladded
43217          * Fires when a panel is added. 
43218          * @param {Roo.LayoutRegion} this
43219          * @param {Roo.ContentPanel} panel The panel
43220          */
43221         "paneladded" : true,
43222         /**
43223          * @event panelremoved
43224          * Fires when a panel is removed. 
43225          * @param {Roo.LayoutRegion} this
43226          * @param {Roo.ContentPanel} panel The panel
43227          */
43228         "panelremoved" : true,
43229         /**
43230          * @event beforecollapse
43231          * Fires when this region before collapse.
43232          * @param {Roo.LayoutRegion} this
43233          */
43234         "beforecollapse" : true,
43235         /**
43236          * @event collapsed
43237          * Fires when this region is collapsed.
43238          * @param {Roo.LayoutRegion} this
43239          */
43240         "collapsed" : true,
43241         /**
43242          * @event expanded
43243          * Fires when this region is expanded.
43244          * @param {Roo.LayoutRegion} this
43245          */
43246         "expanded" : true,
43247         /**
43248          * @event slideshow
43249          * Fires when this region is slid into view.
43250          * @param {Roo.LayoutRegion} this
43251          */
43252         "slideshow" : true,
43253         /**
43254          * @event slidehide
43255          * Fires when this region slides out of view. 
43256          * @param {Roo.LayoutRegion} this
43257          */
43258         "slidehide" : true,
43259         /**
43260          * @event panelactivated
43261          * Fires when a panel is activated. 
43262          * @param {Roo.LayoutRegion} this
43263          * @param {Roo.ContentPanel} panel The activated panel
43264          */
43265         "panelactivated" : true,
43266         /**
43267          * @event resized
43268          * Fires when the user resizes this region. 
43269          * @param {Roo.LayoutRegion} this
43270          * @param {Number} newSize The new size (width for east/west, height for north/south)
43271          */
43272         "resized" : true
43273     };
43274     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43275     this.panels = new Roo.util.MixedCollection();
43276     this.panels.getKey = this.getPanelId.createDelegate(this);
43277     this.box = null;
43278     this.activePanel = null;
43279     // ensure listeners are added...
43280     
43281     if (config.listeners || config.events) {
43282         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43283             listeners : config.listeners || {},
43284             events : config.events || {}
43285         });
43286     }
43287     
43288     if(skipConfig !== true){
43289         this.applyConfig(config);
43290     }
43291 };
43292
43293 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43294 {
43295     getPanelId : function(p){
43296         return p.getId();
43297     },
43298     
43299     applyConfig : function(config){
43300         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43301         this.config = config;
43302         
43303     },
43304     
43305     /**
43306      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43307      * the width, for horizontal (north, south) the height.
43308      * @param {Number} newSize The new width or height
43309      */
43310     resizeTo : function(newSize){
43311         var el = this.el ? this.el :
43312                  (this.activePanel ? this.activePanel.getEl() : null);
43313         if(el){
43314             switch(this.position){
43315                 case "east":
43316                 case "west":
43317                     el.setWidth(newSize);
43318                     this.fireEvent("resized", this, newSize);
43319                 break;
43320                 case "north":
43321                 case "south":
43322                     el.setHeight(newSize);
43323                     this.fireEvent("resized", this, newSize);
43324                 break;                
43325             }
43326         }
43327     },
43328     
43329     getBox : function(){
43330         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43331     },
43332     
43333     getMargins : function(){
43334         return this.margins;
43335     },
43336     
43337     updateBox : function(box){
43338         this.box = box;
43339         var el = this.activePanel.getEl();
43340         el.dom.style.left = box.x + "px";
43341         el.dom.style.top = box.y + "px";
43342         this.activePanel.setSize(box.width, box.height);
43343     },
43344     
43345     /**
43346      * Returns the container element for this region.
43347      * @return {Roo.Element}
43348      */
43349     getEl : function(){
43350         return this.activePanel;
43351     },
43352     
43353     /**
43354      * Returns true if this region is currently visible.
43355      * @return {Boolean}
43356      */
43357     isVisible : function(){
43358         return this.activePanel ? true : false;
43359     },
43360     
43361     setActivePanel : function(panel){
43362         panel = this.getPanel(panel);
43363         if(this.activePanel && this.activePanel != panel){
43364             this.activePanel.setActiveState(false);
43365             this.activePanel.getEl().setLeftTop(-10000,-10000);
43366         }
43367         this.activePanel = panel;
43368         panel.setActiveState(true);
43369         if(this.box){
43370             panel.setSize(this.box.width, this.box.height);
43371         }
43372         this.fireEvent("panelactivated", this, panel);
43373         this.fireEvent("invalidated");
43374     },
43375     
43376     /**
43377      * Show the specified panel.
43378      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43379      * @return {Roo.ContentPanel} The shown panel or null
43380      */
43381     showPanel : function(panel){
43382         panel = this.getPanel(panel);
43383         if(panel){
43384             this.setActivePanel(panel);
43385         }
43386         return panel;
43387     },
43388     
43389     /**
43390      * Get the active panel for this region.
43391      * @return {Roo.ContentPanel} The active panel or null
43392      */
43393     getActivePanel : function(){
43394         return this.activePanel;
43395     },
43396     
43397     /**
43398      * Add the passed ContentPanel(s)
43399      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43400      * @return {Roo.ContentPanel} The panel added (if only one was added)
43401      */
43402     add : function(panel){
43403         if(arguments.length > 1){
43404             for(var i = 0, len = arguments.length; i < len; i++) {
43405                 this.add(arguments[i]);
43406             }
43407             return null;
43408         }
43409         if(this.hasPanel(panel)){
43410             this.showPanel(panel);
43411             return panel;
43412         }
43413         var el = panel.getEl();
43414         if(el.dom.parentNode != this.mgr.el.dom){
43415             this.mgr.el.dom.appendChild(el.dom);
43416         }
43417         if(panel.setRegion){
43418             panel.setRegion(this);
43419         }
43420         this.panels.add(panel);
43421         el.setStyle("position", "absolute");
43422         if(!panel.background){
43423             this.setActivePanel(panel);
43424             if(this.config.initialSize && this.panels.getCount()==1){
43425                 this.resizeTo(this.config.initialSize);
43426             }
43427         }
43428         this.fireEvent("paneladded", this, panel);
43429         return panel;
43430     },
43431     
43432     /**
43433      * Returns true if the panel is in this region.
43434      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43435      * @return {Boolean}
43436      */
43437     hasPanel : function(panel){
43438         if(typeof panel == "object"){ // must be panel obj
43439             panel = panel.getId();
43440         }
43441         return this.getPanel(panel) ? true : false;
43442     },
43443     
43444     /**
43445      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43446      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43447      * @param {Boolean} preservePanel Overrides the config preservePanel option
43448      * @return {Roo.ContentPanel} The panel that was removed
43449      */
43450     remove : function(panel, preservePanel){
43451         panel = this.getPanel(panel);
43452         if(!panel){
43453             return null;
43454         }
43455         var e = {};
43456         this.fireEvent("beforeremove", this, panel, e);
43457         if(e.cancel === true){
43458             return null;
43459         }
43460         var panelId = panel.getId();
43461         this.panels.removeKey(panelId);
43462         return panel;
43463     },
43464     
43465     /**
43466      * Returns the panel specified or null if it's not in this region.
43467      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43468      * @return {Roo.ContentPanel}
43469      */
43470     getPanel : function(id){
43471         if(typeof id == "object"){ // must be panel obj
43472             return id;
43473         }
43474         return this.panels.get(id);
43475     },
43476     
43477     /**
43478      * Returns this regions position (north/south/east/west/center).
43479      * @return {String} 
43480      */
43481     getPosition: function(){
43482         return this.position;    
43483     }
43484 });/*
43485  * Based on:
43486  * Ext JS Library 1.1.1
43487  * Copyright(c) 2006-2007, Ext JS, LLC.
43488  *
43489  * Originally Released Under LGPL - original licence link has changed is not relivant.
43490  *
43491  * Fork - LGPL
43492  * <script type="text/javascript">
43493  */
43494  
43495 /**
43496  * @class Roo.bootstrap.layout.Region
43497  * @extends Roo.bootstrap.layout.Basic
43498  * This class represents a region in a layout manager.
43499  
43500  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43501  * @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})
43502  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43503  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43504  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43505  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43506  * @cfg {String}    title           The title for the region (overrides panel titles)
43507  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43508  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43509  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43510  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43511  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43512  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43513  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43514  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43515  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43516  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43517
43518  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43519  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43520  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43521  * @cfg {Number}    width           For East/West panels
43522  * @cfg {Number}    height          For North/South panels
43523  * @cfg {Boolean}   split           To show the splitter
43524  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43525  * 
43526  * @cfg {string}   cls             Extra CSS classes to add to region
43527  * 
43528  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43529  * @cfg {string}   region  the region that it inhabits..
43530  *
43531
43532  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43533  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43534
43535  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43536  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43537  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43538  */
43539 Roo.bootstrap.layout.Region = function(config)
43540 {
43541     this.applyConfig(config);
43542
43543     var mgr = config.mgr;
43544     var pos = config.region;
43545     config.skipConfig = true;
43546     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43547     
43548     if (mgr.el) {
43549         this.onRender(mgr.el);   
43550     }
43551      
43552     this.visible = true;
43553     this.collapsed = false;
43554     this.unrendered_panels = [];
43555 };
43556
43557 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43558
43559     position: '', // set by wrapper (eg. north/south etc..)
43560     unrendered_panels : null,  // unrendered panels.
43561     
43562     tabPosition : false,
43563     
43564     mgr: false, // points to 'Border'
43565     
43566     
43567     createBody : function(){
43568         /** This region's body element 
43569         * @type Roo.Element */
43570         this.bodyEl = this.el.createChild({
43571                 tag: "div",
43572                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43573         });
43574     },
43575
43576     onRender: function(ctr, pos)
43577     {
43578         var dh = Roo.DomHelper;
43579         /** This region's container element 
43580         * @type Roo.Element */
43581         this.el = dh.append(ctr.dom, {
43582                 tag: "div",
43583                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43584             }, true);
43585         /** This region's title element 
43586         * @type Roo.Element */
43587     
43588         this.titleEl = dh.append(this.el.dom,  {
43589                 tag: "div",
43590                 unselectable: "on",
43591                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43592                 children:[
43593                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43594                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43595                 ]
43596             }, true);
43597         
43598         this.titleEl.enableDisplayMode();
43599         /** This region's title text element 
43600         * @type HTMLElement */
43601         this.titleTextEl = this.titleEl.dom.firstChild;
43602         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43603         /*
43604         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43605         this.closeBtn.enableDisplayMode();
43606         this.closeBtn.on("click", this.closeClicked, this);
43607         this.closeBtn.hide();
43608     */
43609         this.createBody(this.config);
43610         if(this.config.hideWhenEmpty){
43611             this.hide();
43612             this.on("paneladded", this.validateVisibility, this);
43613             this.on("panelremoved", this.validateVisibility, this);
43614         }
43615         if(this.autoScroll){
43616             this.bodyEl.setStyle("overflow", "auto");
43617         }else{
43618             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43619         }
43620         //if(c.titlebar !== false){
43621             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43622                 this.titleEl.hide();
43623             }else{
43624                 this.titleEl.show();
43625                 if(this.config.title){
43626                     this.titleTextEl.innerHTML = this.config.title;
43627                 }
43628             }
43629         //}
43630         if(this.config.collapsed){
43631             this.collapse(true);
43632         }
43633         if(this.config.hidden){
43634             this.hide();
43635         }
43636         
43637         if (this.unrendered_panels && this.unrendered_panels.length) {
43638             for (var i =0;i< this.unrendered_panels.length; i++) {
43639                 this.add(this.unrendered_panels[i]);
43640             }
43641             this.unrendered_panels = null;
43642             
43643         }
43644         
43645     },
43646     
43647     applyConfig : function(c)
43648     {
43649         /*
43650          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43651             var dh = Roo.DomHelper;
43652             if(c.titlebar !== false){
43653                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43654                 this.collapseBtn.on("click", this.collapse, this);
43655                 this.collapseBtn.enableDisplayMode();
43656                 /*
43657                 if(c.showPin === true || this.showPin){
43658                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43659                     this.stickBtn.enableDisplayMode();
43660                     this.stickBtn.on("click", this.expand, this);
43661                     this.stickBtn.hide();
43662                 }
43663                 
43664             }
43665             */
43666             /** This region's collapsed element
43667             * @type Roo.Element */
43668             /*
43669              *
43670             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43671                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43672             ]}, true);
43673             
43674             if(c.floatable !== false){
43675                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43676                this.collapsedEl.on("click", this.collapseClick, this);
43677             }
43678
43679             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43680                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43681                    id: "message", unselectable: "on", style:{"float":"left"}});
43682                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43683              }
43684             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43685             this.expandBtn.on("click", this.expand, this);
43686             
43687         }
43688         
43689         if(this.collapseBtn){
43690             this.collapseBtn.setVisible(c.collapsible == true);
43691         }
43692         
43693         this.cmargins = c.cmargins || this.cmargins ||
43694                          (this.position == "west" || this.position == "east" ?
43695                              {top: 0, left: 2, right:2, bottom: 0} :
43696                              {top: 2, left: 0, right:0, bottom: 2});
43697         */
43698         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43699         
43700         
43701         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43702         
43703         this.autoScroll = c.autoScroll || false;
43704         
43705         
43706        
43707         
43708         this.duration = c.duration || .30;
43709         this.slideDuration = c.slideDuration || .45;
43710         this.config = c;
43711        
43712     },
43713     /**
43714      * Returns true if this region is currently visible.
43715      * @return {Boolean}
43716      */
43717     isVisible : function(){
43718         return this.visible;
43719     },
43720
43721     /**
43722      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43723      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43724      */
43725     //setCollapsedTitle : function(title){
43726     //    title = title || "&#160;";
43727      //   if(this.collapsedTitleTextEl){
43728       //      this.collapsedTitleTextEl.innerHTML = title;
43729        // }
43730     //},
43731
43732     getBox : function(){
43733         var b;
43734       //  if(!this.collapsed){
43735             b = this.el.getBox(false, true);
43736        // }else{
43737           //  b = this.collapsedEl.getBox(false, true);
43738         //}
43739         return b;
43740     },
43741
43742     getMargins : function(){
43743         return this.margins;
43744         //return this.collapsed ? this.cmargins : this.margins;
43745     },
43746 /*
43747     highlight : function(){
43748         this.el.addClass("x-layout-panel-dragover");
43749     },
43750
43751     unhighlight : function(){
43752         this.el.removeClass("x-layout-panel-dragover");
43753     },
43754 */
43755     updateBox : function(box)
43756     {
43757         if (!this.bodyEl) {
43758             return; // not rendered yet..
43759         }
43760         
43761         this.box = box;
43762         if(!this.collapsed){
43763             this.el.dom.style.left = box.x + "px";
43764             this.el.dom.style.top = box.y + "px";
43765             this.updateBody(box.width, box.height);
43766         }else{
43767             this.collapsedEl.dom.style.left = box.x + "px";
43768             this.collapsedEl.dom.style.top = box.y + "px";
43769             this.collapsedEl.setSize(box.width, box.height);
43770         }
43771         if(this.tabs){
43772             this.tabs.autoSizeTabs();
43773         }
43774     },
43775
43776     updateBody : function(w, h)
43777     {
43778         if(w !== null){
43779             this.el.setWidth(w);
43780             w -= this.el.getBorderWidth("rl");
43781             if(this.config.adjustments){
43782                 w += this.config.adjustments[0];
43783             }
43784         }
43785         if(h !== null && h > 0){
43786             this.el.setHeight(h);
43787             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43788             h -= this.el.getBorderWidth("tb");
43789             if(this.config.adjustments){
43790                 h += this.config.adjustments[1];
43791             }
43792             this.bodyEl.setHeight(h);
43793             if(this.tabs){
43794                 h = this.tabs.syncHeight(h);
43795             }
43796         }
43797         if(this.panelSize){
43798             w = w !== null ? w : this.panelSize.width;
43799             h = h !== null ? h : this.panelSize.height;
43800         }
43801         if(this.activePanel){
43802             var el = this.activePanel.getEl();
43803             w = w !== null ? w : el.getWidth();
43804             h = h !== null ? h : el.getHeight();
43805             this.panelSize = {width: w, height: h};
43806             this.activePanel.setSize(w, h);
43807         }
43808         if(Roo.isIE && this.tabs){
43809             this.tabs.el.repaint();
43810         }
43811     },
43812
43813     /**
43814      * Returns the container element for this region.
43815      * @return {Roo.Element}
43816      */
43817     getEl : function(){
43818         return this.el;
43819     },
43820
43821     /**
43822      * Hides this region.
43823      */
43824     hide : function(){
43825         //if(!this.collapsed){
43826             this.el.dom.style.left = "-2000px";
43827             this.el.hide();
43828         //}else{
43829          //   this.collapsedEl.dom.style.left = "-2000px";
43830          //   this.collapsedEl.hide();
43831        // }
43832         this.visible = false;
43833         this.fireEvent("visibilitychange", this, false);
43834     },
43835
43836     /**
43837      * Shows this region if it was previously hidden.
43838      */
43839     show : function(){
43840         //if(!this.collapsed){
43841             this.el.show();
43842         //}else{
43843         //    this.collapsedEl.show();
43844        // }
43845         this.visible = true;
43846         this.fireEvent("visibilitychange", this, true);
43847     },
43848 /*
43849     closeClicked : function(){
43850         if(this.activePanel){
43851             this.remove(this.activePanel);
43852         }
43853     },
43854
43855     collapseClick : function(e){
43856         if(this.isSlid){
43857            e.stopPropagation();
43858            this.slideIn();
43859         }else{
43860            e.stopPropagation();
43861            this.slideOut();
43862         }
43863     },
43864 */
43865     /**
43866      * Collapses this region.
43867      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43868      */
43869     /*
43870     collapse : function(skipAnim, skipCheck = false){
43871         if(this.collapsed) {
43872             return;
43873         }
43874         
43875         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43876             
43877             this.collapsed = true;
43878             if(this.split){
43879                 this.split.el.hide();
43880             }
43881             if(this.config.animate && skipAnim !== true){
43882                 this.fireEvent("invalidated", this);
43883                 this.animateCollapse();
43884             }else{
43885                 this.el.setLocation(-20000,-20000);
43886                 this.el.hide();
43887                 this.collapsedEl.show();
43888                 this.fireEvent("collapsed", this);
43889                 this.fireEvent("invalidated", this);
43890             }
43891         }
43892         
43893     },
43894 */
43895     animateCollapse : function(){
43896         // overridden
43897     },
43898
43899     /**
43900      * Expands this region if it was previously collapsed.
43901      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43902      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43903      */
43904     /*
43905     expand : function(e, skipAnim){
43906         if(e) {
43907             e.stopPropagation();
43908         }
43909         if(!this.collapsed || this.el.hasActiveFx()) {
43910             return;
43911         }
43912         if(this.isSlid){
43913             this.afterSlideIn();
43914             skipAnim = true;
43915         }
43916         this.collapsed = false;
43917         if(this.config.animate && skipAnim !== true){
43918             this.animateExpand();
43919         }else{
43920             this.el.show();
43921             if(this.split){
43922                 this.split.el.show();
43923             }
43924             this.collapsedEl.setLocation(-2000,-2000);
43925             this.collapsedEl.hide();
43926             this.fireEvent("invalidated", this);
43927             this.fireEvent("expanded", this);
43928         }
43929     },
43930 */
43931     animateExpand : function(){
43932         // overridden
43933     },
43934
43935     initTabs : function()
43936     {
43937         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43938         
43939         var ts = new Roo.bootstrap.panel.Tabs({
43940             el: this.bodyEl.dom,
43941             region : this,
43942             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43943             disableTooltips: this.config.disableTabTips,
43944             toolbar : this.config.toolbar
43945         });
43946         
43947         if(this.config.hideTabs){
43948             ts.stripWrap.setDisplayed(false);
43949         }
43950         this.tabs = ts;
43951         ts.resizeTabs = this.config.resizeTabs === true;
43952         ts.minTabWidth = this.config.minTabWidth || 40;
43953         ts.maxTabWidth = this.config.maxTabWidth || 250;
43954         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43955         ts.monitorResize = false;
43956         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43957         ts.bodyEl.addClass('roo-layout-tabs-body');
43958         this.panels.each(this.initPanelAsTab, this);
43959     },
43960
43961     initPanelAsTab : function(panel){
43962         var ti = this.tabs.addTab(
43963             panel.getEl().id,
43964             panel.getTitle(),
43965             null,
43966             this.config.closeOnTab && panel.isClosable(),
43967             panel.tpl
43968         );
43969         if(panel.tabTip !== undefined){
43970             ti.setTooltip(panel.tabTip);
43971         }
43972         ti.on("activate", function(){
43973               this.setActivePanel(panel);
43974         }, this);
43975         
43976         if(this.config.closeOnTab){
43977             ti.on("beforeclose", function(t, e){
43978                 e.cancel = true;
43979                 this.remove(panel);
43980             }, this);
43981         }
43982         
43983         panel.tabItem = ti;
43984         
43985         return ti;
43986     },
43987
43988     updatePanelTitle : function(panel, title)
43989     {
43990         if(this.activePanel == panel){
43991             this.updateTitle(title);
43992         }
43993         if(this.tabs){
43994             var ti = this.tabs.getTab(panel.getEl().id);
43995             ti.setText(title);
43996             if(panel.tabTip !== undefined){
43997                 ti.setTooltip(panel.tabTip);
43998             }
43999         }
44000     },
44001
44002     updateTitle : function(title){
44003         if(this.titleTextEl && !this.config.title){
44004             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44005         }
44006     },
44007
44008     setActivePanel : function(panel)
44009     {
44010         panel = this.getPanel(panel);
44011         if(this.activePanel && this.activePanel != panel){
44012             if(this.activePanel.setActiveState(false) === false){
44013                 return;
44014             }
44015         }
44016         this.activePanel = panel;
44017         panel.setActiveState(true);
44018         if(this.panelSize){
44019             panel.setSize(this.panelSize.width, this.panelSize.height);
44020         }
44021         if(this.closeBtn){
44022             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44023         }
44024         this.updateTitle(panel.getTitle());
44025         if(this.tabs){
44026             this.fireEvent("invalidated", this);
44027         }
44028         this.fireEvent("panelactivated", this, panel);
44029     },
44030
44031     /**
44032      * Shows the specified panel.
44033      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44034      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44035      */
44036     showPanel : function(panel)
44037     {
44038         panel = this.getPanel(panel);
44039         if(panel){
44040             if(this.tabs){
44041                 var tab = this.tabs.getTab(panel.getEl().id);
44042                 if(tab.isHidden()){
44043                     this.tabs.unhideTab(tab.id);
44044                 }
44045                 tab.activate();
44046             }else{
44047                 this.setActivePanel(panel);
44048             }
44049         }
44050         return panel;
44051     },
44052
44053     /**
44054      * Get the active panel for this region.
44055      * @return {Roo.ContentPanel} The active panel or null
44056      */
44057     getActivePanel : function(){
44058         return this.activePanel;
44059     },
44060
44061     validateVisibility : function(){
44062         if(this.panels.getCount() < 1){
44063             this.updateTitle("&#160;");
44064             this.closeBtn.hide();
44065             this.hide();
44066         }else{
44067             if(!this.isVisible()){
44068                 this.show();
44069             }
44070         }
44071     },
44072
44073     /**
44074      * Adds the passed ContentPanel(s) to this region.
44075      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44076      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44077      */
44078     add : function(panel)
44079     {
44080         if(arguments.length > 1){
44081             for(var i = 0, len = arguments.length; i < len; i++) {
44082                 this.add(arguments[i]);
44083             }
44084             return null;
44085         }
44086         
44087         // if we have not been rendered yet, then we can not really do much of this..
44088         if (!this.bodyEl) {
44089             this.unrendered_panels.push(panel);
44090             return panel;
44091         }
44092         
44093         
44094         
44095         
44096         if(this.hasPanel(panel)){
44097             this.showPanel(panel);
44098             return panel;
44099         }
44100         panel.setRegion(this);
44101         this.panels.add(panel);
44102        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44103             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44104             // and hide them... ???
44105             this.bodyEl.dom.appendChild(panel.getEl().dom);
44106             if(panel.background !== true){
44107                 this.setActivePanel(panel);
44108             }
44109             this.fireEvent("paneladded", this, panel);
44110             return panel;
44111         }
44112         */
44113         if(!this.tabs){
44114             this.initTabs();
44115         }else{
44116             this.initPanelAsTab(panel);
44117         }
44118         
44119         
44120         if(panel.background !== true){
44121             this.tabs.activate(panel.getEl().id);
44122         }
44123         this.fireEvent("paneladded", this, panel);
44124         return panel;
44125     },
44126
44127     /**
44128      * Hides the tab for the specified panel.
44129      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44130      */
44131     hidePanel : function(panel){
44132         if(this.tabs && (panel = this.getPanel(panel))){
44133             this.tabs.hideTab(panel.getEl().id);
44134         }
44135     },
44136
44137     /**
44138      * Unhides the tab for a previously hidden panel.
44139      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44140      */
44141     unhidePanel : function(panel){
44142         if(this.tabs && (panel = this.getPanel(panel))){
44143             this.tabs.unhideTab(panel.getEl().id);
44144         }
44145     },
44146
44147     clearPanels : function(){
44148         while(this.panels.getCount() > 0){
44149              this.remove(this.panels.first());
44150         }
44151     },
44152
44153     /**
44154      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44155      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44156      * @param {Boolean} preservePanel Overrides the config preservePanel option
44157      * @return {Roo.ContentPanel} The panel that was removed
44158      */
44159     remove : function(panel, preservePanel)
44160     {
44161         panel = this.getPanel(panel);
44162         if(!panel){
44163             return null;
44164         }
44165         var e = {};
44166         this.fireEvent("beforeremove", this, panel, e);
44167         if(e.cancel === true){
44168             return null;
44169         }
44170         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44171         var panelId = panel.getId();
44172         this.panels.removeKey(panelId);
44173         if(preservePanel){
44174             document.body.appendChild(panel.getEl().dom);
44175         }
44176         if(this.tabs){
44177             this.tabs.removeTab(panel.getEl().id);
44178         }else if (!preservePanel){
44179             this.bodyEl.dom.removeChild(panel.getEl().dom);
44180         }
44181         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44182             var p = this.panels.first();
44183             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44184             tempEl.appendChild(p.getEl().dom);
44185             this.bodyEl.update("");
44186             this.bodyEl.dom.appendChild(p.getEl().dom);
44187             tempEl = null;
44188             this.updateTitle(p.getTitle());
44189             this.tabs = null;
44190             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44191             this.setActivePanel(p);
44192         }
44193         panel.setRegion(null);
44194         if(this.activePanel == panel){
44195             this.activePanel = null;
44196         }
44197         if(this.config.autoDestroy !== false && preservePanel !== true){
44198             try{panel.destroy();}catch(e){}
44199         }
44200         this.fireEvent("panelremoved", this, panel);
44201         return panel;
44202     },
44203
44204     /**
44205      * Returns the TabPanel component used by this region
44206      * @return {Roo.TabPanel}
44207      */
44208     getTabs : function(){
44209         return this.tabs;
44210     },
44211
44212     createTool : function(parentEl, className){
44213         var btn = Roo.DomHelper.append(parentEl, {
44214             tag: "div",
44215             cls: "x-layout-tools-button",
44216             children: [ {
44217                 tag: "div",
44218                 cls: "roo-layout-tools-button-inner " + className,
44219                 html: "&#160;"
44220             }]
44221         }, true);
44222         btn.addClassOnOver("roo-layout-tools-button-over");
44223         return btn;
44224     }
44225 });/*
44226  * Based on:
44227  * Ext JS Library 1.1.1
44228  * Copyright(c) 2006-2007, Ext JS, LLC.
44229  *
44230  * Originally Released Under LGPL - original licence link has changed is not relivant.
44231  *
44232  * Fork - LGPL
44233  * <script type="text/javascript">
44234  */
44235  
44236
44237
44238 /**
44239  * @class Roo.SplitLayoutRegion
44240  * @extends Roo.LayoutRegion
44241  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44242  */
44243 Roo.bootstrap.layout.Split = function(config){
44244     this.cursor = config.cursor;
44245     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44246 };
44247
44248 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44249 {
44250     splitTip : "Drag to resize.",
44251     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44252     useSplitTips : false,
44253
44254     applyConfig : function(config){
44255         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44256     },
44257     
44258     onRender : function(ctr,pos) {
44259         
44260         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44261         if(!this.config.split){
44262             return;
44263         }
44264         if(!this.split){
44265             
44266             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44267                             tag: "div",
44268                             id: this.el.id + "-split",
44269                             cls: "roo-layout-split roo-layout-split-"+this.position,
44270                             html: "&#160;"
44271             });
44272             /** The SplitBar for this region 
44273             * @type Roo.SplitBar */
44274             // does not exist yet...
44275             Roo.log([this.position, this.orientation]);
44276             
44277             this.split = new Roo.bootstrap.SplitBar({
44278                 dragElement : splitEl,
44279                 resizingElement: this.el,
44280                 orientation : this.orientation
44281             });
44282             
44283             this.split.on("moved", this.onSplitMove, this);
44284             this.split.useShim = this.config.useShim === true;
44285             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44286             if(this.useSplitTips){
44287                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44288             }
44289             //if(config.collapsible){
44290             //    this.split.el.on("dblclick", this.collapse,  this);
44291             //}
44292         }
44293         if(typeof this.config.minSize != "undefined"){
44294             this.split.minSize = this.config.minSize;
44295         }
44296         if(typeof this.config.maxSize != "undefined"){
44297             this.split.maxSize = this.config.maxSize;
44298         }
44299         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44300             this.hideSplitter();
44301         }
44302         
44303     },
44304
44305     getHMaxSize : function(){
44306          var cmax = this.config.maxSize || 10000;
44307          var center = this.mgr.getRegion("center");
44308          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44309     },
44310
44311     getVMaxSize : function(){
44312          var cmax = this.config.maxSize || 10000;
44313          var center = this.mgr.getRegion("center");
44314          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44315     },
44316
44317     onSplitMove : function(split, newSize){
44318         this.fireEvent("resized", this, newSize);
44319     },
44320     
44321     /** 
44322      * Returns the {@link Roo.SplitBar} for this region.
44323      * @return {Roo.SplitBar}
44324      */
44325     getSplitBar : function(){
44326         return this.split;
44327     },
44328     
44329     hide : function(){
44330         this.hideSplitter();
44331         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44332     },
44333
44334     hideSplitter : function(){
44335         if(this.split){
44336             this.split.el.setLocation(-2000,-2000);
44337             this.split.el.hide();
44338         }
44339     },
44340
44341     show : function(){
44342         if(this.split){
44343             this.split.el.show();
44344         }
44345         Roo.bootstrap.layout.Split.superclass.show.call(this);
44346     },
44347     
44348     beforeSlide: function(){
44349         if(Roo.isGecko){// firefox overflow auto bug workaround
44350             this.bodyEl.clip();
44351             if(this.tabs) {
44352                 this.tabs.bodyEl.clip();
44353             }
44354             if(this.activePanel){
44355                 this.activePanel.getEl().clip();
44356                 
44357                 if(this.activePanel.beforeSlide){
44358                     this.activePanel.beforeSlide();
44359                 }
44360             }
44361         }
44362     },
44363     
44364     afterSlide : function(){
44365         if(Roo.isGecko){// firefox overflow auto bug workaround
44366             this.bodyEl.unclip();
44367             if(this.tabs) {
44368                 this.tabs.bodyEl.unclip();
44369             }
44370             if(this.activePanel){
44371                 this.activePanel.getEl().unclip();
44372                 if(this.activePanel.afterSlide){
44373                     this.activePanel.afterSlide();
44374                 }
44375             }
44376         }
44377     },
44378
44379     initAutoHide : function(){
44380         if(this.autoHide !== false){
44381             if(!this.autoHideHd){
44382                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44383                 this.autoHideHd = {
44384                     "mouseout": function(e){
44385                         if(!e.within(this.el, true)){
44386                             st.delay(500);
44387                         }
44388                     },
44389                     "mouseover" : function(e){
44390                         st.cancel();
44391                     },
44392                     scope : this
44393                 };
44394             }
44395             this.el.on(this.autoHideHd);
44396         }
44397     },
44398
44399     clearAutoHide : function(){
44400         if(this.autoHide !== false){
44401             this.el.un("mouseout", this.autoHideHd.mouseout);
44402             this.el.un("mouseover", this.autoHideHd.mouseover);
44403         }
44404     },
44405
44406     clearMonitor : function(){
44407         Roo.get(document).un("click", this.slideInIf, this);
44408     },
44409
44410     // these names are backwards but not changed for compat
44411     slideOut : function(){
44412         if(this.isSlid || this.el.hasActiveFx()){
44413             return;
44414         }
44415         this.isSlid = true;
44416         if(this.collapseBtn){
44417             this.collapseBtn.hide();
44418         }
44419         this.closeBtnState = this.closeBtn.getStyle('display');
44420         this.closeBtn.hide();
44421         if(this.stickBtn){
44422             this.stickBtn.show();
44423         }
44424         this.el.show();
44425         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44426         this.beforeSlide();
44427         this.el.setStyle("z-index", 10001);
44428         this.el.slideIn(this.getSlideAnchor(), {
44429             callback: function(){
44430                 this.afterSlide();
44431                 this.initAutoHide();
44432                 Roo.get(document).on("click", this.slideInIf, this);
44433                 this.fireEvent("slideshow", this);
44434             },
44435             scope: this,
44436             block: true
44437         });
44438     },
44439
44440     afterSlideIn : function(){
44441         this.clearAutoHide();
44442         this.isSlid = false;
44443         this.clearMonitor();
44444         this.el.setStyle("z-index", "");
44445         if(this.collapseBtn){
44446             this.collapseBtn.show();
44447         }
44448         this.closeBtn.setStyle('display', this.closeBtnState);
44449         if(this.stickBtn){
44450             this.stickBtn.hide();
44451         }
44452         this.fireEvent("slidehide", this);
44453     },
44454
44455     slideIn : function(cb){
44456         if(!this.isSlid || this.el.hasActiveFx()){
44457             Roo.callback(cb);
44458             return;
44459         }
44460         this.isSlid = false;
44461         this.beforeSlide();
44462         this.el.slideOut(this.getSlideAnchor(), {
44463             callback: function(){
44464                 this.el.setLeftTop(-10000, -10000);
44465                 this.afterSlide();
44466                 this.afterSlideIn();
44467                 Roo.callback(cb);
44468             },
44469             scope: this,
44470             block: true
44471         });
44472     },
44473     
44474     slideInIf : function(e){
44475         if(!e.within(this.el)){
44476             this.slideIn();
44477         }
44478     },
44479
44480     animateCollapse : function(){
44481         this.beforeSlide();
44482         this.el.setStyle("z-index", 20000);
44483         var anchor = this.getSlideAnchor();
44484         this.el.slideOut(anchor, {
44485             callback : function(){
44486                 this.el.setStyle("z-index", "");
44487                 this.collapsedEl.slideIn(anchor, {duration:.3});
44488                 this.afterSlide();
44489                 this.el.setLocation(-10000,-10000);
44490                 this.el.hide();
44491                 this.fireEvent("collapsed", this);
44492             },
44493             scope: this,
44494             block: true
44495         });
44496     },
44497
44498     animateExpand : function(){
44499         this.beforeSlide();
44500         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44501         this.el.setStyle("z-index", 20000);
44502         this.collapsedEl.hide({
44503             duration:.1
44504         });
44505         this.el.slideIn(this.getSlideAnchor(), {
44506             callback : function(){
44507                 this.el.setStyle("z-index", "");
44508                 this.afterSlide();
44509                 if(this.split){
44510                     this.split.el.show();
44511                 }
44512                 this.fireEvent("invalidated", this);
44513                 this.fireEvent("expanded", this);
44514             },
44515             scope: this,
44516             block: true
44517         });
44518     },
44519
44520     anchors : {
44521         "west" : "left",
44522         "east" : "right",
44523         "north" : "top",
44524         "south" : "bottom"
44525     },
44526
44527     sanchors : {
44528         "west" : "l",
44529         "east" : "r",
44530         "north" : "t",
44531         "south" : "b"
44532     },
44533
44534     canchors : {
44535         "west" : "tl-tr",
44536         "east" : "tr-tl",
44537         "north" : "tl-bl",
44538         "south" : "bl-tl"
44539     },
44540
44541     getAnchor : function(){
44542         return this.anchors[this.position];
44543     },
44544
44545     getCollapseAnchor : function(){
44546         return this.canchors[this.position];
44547     },
44548
44549     getSlideAnchor : function(){
44550         return this.sanchors[this.position];
44551     },
44552
44553     getAlignAdj : function(){
44554         var cm = this.cmargins;
44555         switch(this.position){
44556             case "west":
44557                 return [0, 0];
44558             break;
44559             case "east":
44560                 return [0, 0];
44561             break;
44562             case "north":
44563                 return [0, 0];
44564             break;
44565             case "south":
44566                 return [0, 0];
44567             break;
44568         }
44569     },
44570
44571     getExpandAdj : function(){
44572         var c = this.collapsedEl, cm = this.cmargins;
44573         switch(this.position){
44574             case "west":
44575                 return [-(cm.right+c.getWidth()+cm.left), 0];
44576             break;
44577             case "east":
44578                 return [cm.right+c.getWidth()+cm.left, 0];
44579             break;
44580             case "north":
44581                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44582             break;
44583             case "south":
44584                 return [0, cm.top+cm.bottom+c.getHeight()];
44585             break;
44586         }
44587     }
44588 });/*
44589  * Based on:
44590  * Ext JS Library 1.1.1
44591  * Copyright(c) 2006-2007, Ext JS, LLC.
44592  *
44593  * Originally Released Under LGPL - original licence link has changed is not relivant.
44594  *
44595  * Fork - LGPL
44596  * <script type="text/javascript">
44597  */
44598 /*
44599  * These classes are private internal classes
44600  */
44601 Roo.bootstrap.layout.Center = function(config){
44602     config.region = "center";
44603     Roo.bootstrap.layout.Region.call(this, config);
44604     this.visible = true;
44605     this.minWidth = config.minWidth || 20;
44606     this.minHeight = config.minHeight || 20;
44607 };
44608
44609 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44610     hide : function(){
44611         // center panel can't be hidden
44612     },
44613     
44614     show : function(){
44615         // center panel can't be hidden
44616     },
44617     
44618     getMinWidth: function(){
44619         return this.minWidth;
44620     },
44621     
44622     getMinHeight: function(){
44623         return this.minHeight;
44624     }
44625 });
44626
44627
44628
44629
44630  
44631
44632
44633
44634
44635
44636
44637 Roo.bootstrap.layout.North = function(config)
44638 {
44639     config.region = 'north';
44640     config.cursor = 'n-resize';
44641     
44642     Roo.bootstrap.layout.Split.call(this, config);
44643     
44644     
44645     if(this.split){
44646         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44647         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44648         this.split.el.addClass("roo-layout-split-v");
44649     }
44650     //var size = config.initialSize || config.height;
44651     //if(this.el && typeof size != "undefined"){
44652     //    this.el.setHeight(size);
44653     //}
44654 };
44655 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44656 {
44657     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44658      
44659      
44660     onRender : function(ctr, pos)
44661     {
44662         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44663         var size = this.config.initialSize || this.config.height;
44664         if(this.el && typeof size != "undefined"){
44665             this.el.setHeight(size);
44666         }
44667     
44668     },
44669     
44670     getBox : function(){
44671         if(this.collapsed){
44672             return this.collapsedEl.getBox();
44673         }
44674         var box = this.el.getBox();
44675         if(this.split){
44676             box.height += this.split.el.getHeight();
44677         }
44678         return box;
44679     },
44680     
44681     updateBox : function(box){
44682         if(this.split && !this.collapsed){
44683             box.height -= this.split.el.getHeight();
44684             this.split.el.setLeft(box.x);
44685             this.split.el.setTop(box.y+box.height);
44686             this.split.el.setWidth(box.width);
44687         }
44688         if(this.collapsed){
44689             this.updateBody(box.width, null);
44690         }
44691         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44692     }
44693 });
44694
44695
44696
44697
44698
44699 Roo.bootstrap.layout.South = function(config){
44700     config.region = 'south';
44701     config.cursor = 's-resize';
44702     Roo.bootstrap.layout.Split.call(this, config);
44703     if(this.split){
44704         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44705         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44706         this.split.el.addClass("roo-layout-split-v");
44707     }
44708     
44709 };
44710
44711 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44712     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44713     
44714     onRender : function(ctr, pos)
44715     {
44716         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44717         var size = this.config.initialSize || this.config.height;
44718         if(this.el && typeof size != "undefined"){
44719             this.el.setHeight(size);
44720         }
44721     
44722     },
44723     
44724     getBox : function(){
44725         if(this.collapsed){
44726             return this.collapsedEl.getBox();
44727         }
44728         var box = this.el.getBox();
44729         if(this.split){
44730             var sh = this.split.el.getHeight();
44731             box.height += sh;
44732             box.y -= sh;
44733         }
44734         return box;
44735     },
44736     
44737     updateBox : function(box){
44738         if(this.split && !this.collapsed){
44739             var sh = this.split.el.getHeight();
44740             box.height -= sh;
44741             box.y += sh;
44742             this.split.el.setLeft(box.x);
44743             this.split.el.setTop(box.y-sh);
44744             this.split.el.setWidth(box.width);
44745         }
44746         if(this.collapsed){
44747             this.updateBody(box.width, null);
44748         }
44749         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44750     }
44751 });
44752
44753 Roo.bootstrap.layout.East = function(config){
44754     config.region = "east";
44755     config.cursor = "e-resize";
44756     Roo.bootstrap.layout.Split.call(this, config);
44757     if(this.split){
44758         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44759         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44760         this.split.el.addClass("roo-layout-split-h");
44761     }
44762     
44763 };
44764 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44765     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44766     
44767     onRender : function(ctr, pos)
44768     {
44769         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44770         var size = this.config.initialSize || this.config.width;
44771         if(this.el && typeof size != "undefined"){
44772             this.el.setWidth(size);
44773         }
44774     
44775     },
44776     
44777     getBox : function(){
44778         if(this.collapsed){
44779             return this.collapsedEl.getBox();
44780         }
44781         var box = this.el.getBox();
44782         if(this.split){
44783             var sw = this.split.el.getWidth();
44784             box.width += sw;
44785             box.x -= sw;
44786         }
44787         return box;
44788     },
44789
44790     updateBox : function(box){
44791         if(this.split && !this.collapsed){
44792             var sw = this.split.el.getWidth();
44793             box.width -= sw;
44794             this.split.el.setLeft(box.x);
44795             this.split.el.setTop(box.y);
44796             this.split.el.setHeight(box.height);
44797             box.x += sw;
44798         }
44799         if(this.collapsed){
44800             this.updateBody(null, box.height);
44801         }
44802         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44803     }
44804 });
44805
44806 Roo.bootstrap.layout.West = function(config){
44807     config.region = "west";
44808     config.cursor = "w-resize";
44809     
44810     Roo.bootstrap.layout.Split.call(this, config);
44811     if(this.split){
44812         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44813         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44814         this.split.el.addClass("roo-layout-split-h");
44815     }
44816     
44817 };
44818 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44819     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44820     
44821     onRender: function(ctr, pos)
44822     {
44823         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44824         var size = this.config.initialSize || this.config.width;
44825         if(typeof size != "undefined"){
44826             this.el.setWidth(size);
44827         }
44828     },
44829     
44830     getBox : function(){
44831         if(this.collapsed){
44832             return this.collapsedEl.getBox();
44833         }
44834         var box = this.el.getBox();
44835         if (box.width == 0) {
44836             box.width = this.config.width; // kludge?
44837         }
44838         if(this.split){
44839             box.width += this.split.el.getWidth();
44840         }
44841         return box;
44842     },
44843     
44844     updateBox : function(box){
44845         if(this.split && !this.collapsed){
44846             var sw = this.split.el.getWidth();
44847             box.width -= sw;
44848             this.split.el.setLeft(box.x+box.width);
44849             this.split.el.setTop(box.y);
44850             this.split.el.setHeight(box.height);
44851         }
44852         if(this.collapsed){
44853             this.updateBody(null, box.height);
44854         }
44855         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44856     }
44857 });/*
44858  * Based on:
44859  * Ext JS Library 1.1.1
44860  * Copyright(c) 2006-2007, Ext JS, LLC.
44861  *
44862  * Originally Released Under LGPL - original licence link has changed is not relivant.
44863  *
44864  * Fork - LGPL
44865  * <script type="text/javascript">
44866  */
44867 /**
44868  * @class Roo.bootstrap.paenl.Content
44869  * @extends Roo.util.Observable
44870  * @children Roo.bootstrap.Component
44871  * @parent builder Roo.bootstrap.layout.Border
44872  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44873  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44874  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44875  * @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
44876  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44877  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44878  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44879  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44880  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44881  * @cfg {String} title          The title for this panel
44882  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44883  * @cfg {String} url            Calls {@link #setUrl} with this value
44884  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44885  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44886  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44887  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44888  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44889  * @cfg {Boolean} badges render the badges
44890  * @cfg {String} cls  extra classes to use  
44891  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44892  
44893  * @constructor
44894  * Create a new ContentPanel.
44895  * @param {String/Object} config A string to set only the title or a config object
44896  
44897  */
44898 Roo.bootstrap.panel.Content = function( config){
44899     
44900     this.tpl = config.tpl || false;
44901     
44902     var el = config.el;
44903     var content = config.content;
44904
44905     if(config.autoCreate){ // xtype is available if this is called from factory
44906         el = Roo.id();
44907     }
44908     this.el = Roo.get(el);
44909     if(!this.el && config && config.autoCreate){
44910         if(typeof config.autoCreate == "object"){
44911             if(!config.autoCreate.id){
44912                 config.autoCreate.id = config.id||el;
44913             }
44914             this.el = Roo.DomHelper.append(document.body,
44915                         config.autoCreate, true);
44916         }else{
44917             var elcfg =  {
44918                 tag: "div",
44919                 cls: (config.cls || '') +
44920                     (config.background ? ' bg-' + config.background : '') +
44921                     " roo-layout-inactive-content",
44922                 id: config.id||el
44923             };
44924             if (config.iframe) {
44925                 elcfg.cn = [
44926                     {
44927                         tag : 'iframe',
44928                         style : 'border: 0px',
44929                         src : 'about:blank'
44930                     }
44931                 ];
44932             }
44933               
44934             if (config.html) {
44935                 elcfg.html = config.html;
44936                 
44937             }
44938                         
44939             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44940             if (config.iframe) {
44941                 this.iframeEl = this.el.select('iframe',true).first();
44942             }
44943             
44944         }
44945     } 
44946     this.closable = false;
44947     this.loaded = false;
44948     this.active = false;
44949    
44950       
44951     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44952         
44953         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44954         
44955         this.wrapEl = this.el; //this.el.wrap();
44956         var ti = [];
44957         if (config.toolbar.items) {
44958             ti = config.toolbar.items ;
44959             delete config.toolbar.items ;
44960         }
44961         
44962         var nitems = [];
44963         this.toolbar.render(this.wrapEl, 'before');
44964         for(var i =0;i < ti.length;i++) {
44965           //  Roo.log(['add child', items[i]]);
44966             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44967         }
44968         this.toolbar.items = nitems;
44969         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44970         delete config.toolbar;
44971         
44972     }
44973     /*
44974     // xtype created footer. - not sure if will work as we normally have to render first..
44975     if (this.footer && !this.footer.el && this.footer.xtype) {
44976         if (!this.wrapEl) {
44977             this.wrapEl = this.el.wrap();
44978         }
44979     
44980         this.footer.container = this.wrapEl.createChild();
44981          
44982         this.footer = Roo.factory(this.footer, Roo);
44983         
44984     }
44985     */
44986     
44987      if(typeof config == "string"){
44988         this.title = config;
44989     }else{
44990         Roo.apply(this, config);
44991     }
44992     
44993     if(this.resizeEl){
44994         this.resizeEl = Roo.get(this.resizeEl, true);
44995     }else{
44996         this.resizeEl = this.el;
44997     }
44998     // handle view.xtype
44999     
45000  
45001     
45002     
45003     this.addEvents({
45004         /**
45005          * @event activate
45006          * Fires when this panel is activated. 
45007          * @param {Roo.ContentPanel} this
45008          */
45009         "activate" : true,
45010         /**
45011          * @event deactivate
45012          * Fires when this panel is activated. 
45013          * @param {Roo.ContentPanel} this
45014          */
45015         "deactivate" : true,
45016
45017         /**
45018          * @event resize
45019          * Fires when this panel is resized if fitToFrame is true.
45020          * @param {Roo.ContentPanel} this
45021          * @param {Number} width The width after any component adjustments
45022          * @param {Number} height The height after any component adjustments
45023          */
45024         "resize" : true,
45025         
45026          /**
45027          * @event render
45028          * Fires when this tab is created
45029          * @param {Roo.ContentPanel} this
45030          */
45031         "render" : true,
45032         
45033           /**
45034          * @event scroll
45035          * Fires when this content is scrolled
45036          * @param {Roo.ContentPanel} this
45037          * @param {Event} scrollEvent
45038          */
45039         "scroll" : true
45040         
45041         
45042         
45043     });
45044     
45045
45046     
45047     
45048     if(this.autoScroll && !this.iframe){
45049         this.resizeEl.setStyle("overflow", "auto");
45050         this.resizeEl.on('scroll', this.onScroll, this);
45051     } else {
45052         // fix randome scrolling
45053         //this.el.on('scroll', function() {
45054         //    Roo.log('fix random scolling');
45055         //    this.scrollTo('top',0); 
45056         //});
45057     }
45058     content = content || this.content;
45059     if(content){
45060         this.setContent(content);
45061     }
45062     if(config && config.url){
45063         this.setUrl(this.url, this.params, this.loadOnce);
45064     }
45065     
45066     
45067     
45068     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45069     
45070     if (this.view && typeof(this.view.xtype) != 'undefined') {
45071         this.view.el = this.el.appendChild(document.createElement("div"));
45072         this.view = Roo.factory(this.view); 
45073         this.view.render  &&  this.view.render(false, '');  
45074     }
45075     
45076     
45077     this.fireEvent('render', this);
45078 };
45079
45080 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45081     
45082     cls : '',
45083     background : '',
45084     
45085     tabTip : '',
45086     
45087     iframe : false,
45088     iframeEl : false,
45089     
45090     /* Resize Element - use this to work out scroll etc. */
45091     resizeEl : false,
45092     
45093     setRegion : function(region){
45094         this.region = region;
45095         this.setActiveClass(region && !this.background);
45096     },
45097     
45098     
45099     setActiveClass: function(state)
45100     {
45101         if(state){
45102            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45103            this.el.setStyle('position','relative');
45104         }else{
45105            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45106            this.el.setStyle('position', 'absolute');
45107         } 
45108     },
45109     
45110     /**
45111      * Returns the toolbar for this Panel if one was configured. 
45112      * @return {Roo.Toolbar} 
45113      */
45114     getToolbar : function(){
45115         return this.toolbar;
45116     },
45117     
45118     setActiveState : function(active)
45119     {
45120         this.active = active;
45121         this.setActiveClass(active);
45122         if(!active){
45123             if(this.fireEvent("deactivate", this) === false){
45124                 return false;
45125             }
45126             return true;
45127         }
45128         this.fireEvent("activate", this);
45129         return true;
45130     },
45131     /**
45132      * Updates this panel's element (not for iframe)
45133      * @param {String} content The new content
45134      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45135     */
45136     setContent : function(content, loadScripts){
45137         if (this.iframe) {
45138             return;
45139         }
45140         
45141         this.el.update(content, loadScripts);
45142     },
45143
45144     ignoreResize : function(w, h)
45145     {
45146         //return false; // always resize?
45147         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45148             return true;
45149         }else{
45150             this.lastSize = {width: w, height: h};
45151             return false;
45152         }
45153     },
45154     /**
45155      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45156      * @return {Roo.UpdateManager} The UpdateManager
45157      */
45158     getUpdateManager : function(){
45159         if (this.iframe) {
45160             return false;
45161         }
45162         return this.el.getUpdateManager();
45163     },
45164      /**
45165      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45166      * Does not work with IFRAME contents
45167      * @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:
45168 <pre><code>
45169 panel.load({
45170     url: "your-url.php",
45171     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45172     callback: yourFunction,
45173     scope: yourObject, //(optional scope)
45174     discardUrl: false,
45175     nocache: false,
45176     text: "Loading...",
45177     timeout: 30,
45178     scripts: false
45179 });
45180 </code></pre>
45181      
45182      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45183      * 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.
45184      * @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}
45185      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45186      * @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.
45187      * @return {Roo.ContentPanel} this
45188      */
45189     load : function(){
45190         
45191         if (this.iframe) {
45192             return this;
45193         }
45194         
45195         var um = this.el.getUpdateManager();
45196         um.update.apply(um, arguments);
45197         return this;
45198     },
45199
45200
45201     /**
45202      * 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.
45203      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45204      * @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)
45205      * @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)
45206      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45207      */
45208     setUrl : function(url, params, loadOnce){
45209         if (this.iframe) {
45210             this.iframeEl.dom.src = url;
45211             return false;
45212         }
45213         
45214         if(this.refreshDelegate){
45215             this.removeListener("activate", this.refreshDelegate);
45216         }
45217         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45218         this.on("activate", this.refreshDelegate);
45219         return this.el.getUpdateManager();
45220     },
45221     
45222     _handleRefresh : function(url, params, loadOnce){
45223         if(!loadOnce || !this.loaded){
45224             var updater = this.el.getUpdateManager();
45225             updater.update(url, params, this._setLoaded.createDelegate(this));
45226         }
45227     },
45228     
45229     _setLoaded : function(){
45230         this.loaded = true;
45231     }, 
45232     
45233     /**
45234      * Returns this panel's id
45235      * @return {String} 
45236      */
45237     getId : function(){
45238         return this.el.id;
45239     },
45240     
45241     /** 
45242      * Returns this panel's element - used by regiosn to add.
45243      * @return {Roo.Element} 
45244      */
45245     getEl : function(){
45246         return this.wrapEl || this.el;
45247     },
45248     
45249    
45250     
45251     adjustForComponents : function(width, height)
45252     {
45253         //Roo.log('adjustForComponents ');
45254         if(this.resizeEl != this.el){
45255             width -= this.el.getFrameWidth('lr');
45256             height -= this.el.getFrameWidth('tb');
45257         }
45258         if(this.toolbar){
45259             var te = this.toolbar.getEl();
45260             te.setWidth(width);
45261             height -= te.getHeight();
45262         }
45263         if(this.footer){
45264             var te = this.footer.getEl();
45265             te.setWidth(width);
45266             height -= te.getHeight();
45267         }
45268         
45269         
45270         if(this.adjustments){
45271             width += this.adjustments[0];
45272             height += this.adjustments[1];
45273         }
45274         return {"width": width, "height": height};
45275     },
45276     
45277     setSize : function(width, height){
45278         if(this.fitToFrame && !this.ignoreResize(width, height)){
45279             if(this.fitContainer && this.resizeEl != this.el){
45280                 this.el.setSize(width, height);
45281             }
45282             var size = this.adjustForComponents(width, height);
45283             if (this.iframe) {
45284                 this.iframeEl.setSize(width,height);
45285             }
45286             
45287             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45288             this.fireEvent('resize', this, size.width, size.height);
45289             
45290             
45291         }
45292     },
45293     
45294     /**
45295      * Returns this panel's title
45296      * @return {String} 
45297      */
45298     getTitle : function(){
45299         
45300         if (typeof(this.title) != 'object') {
45301             return this.title;
45302         }
45303         
45304         var t = '';
45305         for (var k in this.title) {
45306             if (!this.title.hasOwnProperty(k)) {
45307                 continue;
45308             }
45309             
45310             if (k.indexOf('-') >= 0) {
45311                 var s = k.split('-');
45312                 for (var i = 0; i<s.length; i++) {
45313                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45314                 }
45315             } else {
45316                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45317             }
45318         }
45319         return t;
45320     },
45321     
45322     /**
45323      * Set this panel's title
45324      * @param {String} title
45325      */
45326     setTitle : function(title){
45327         this.title = title;
45328         if(this.region){
45329             this.region.updatePanelTitle(this, title);
45330         }
45331     },
45332     
45333     /**
45334      * Returns true is this panel was configured to be closable
45335      * @return {Boolean} 
45336      */
45337     isClosable : function(){
45338         return this.closable;
45339     },
45340     
45341     beforeSlide : function(){
45342         this.el.clip();
45343         this.resizeEl.clip();
45344     },
45345     
45346     afterSlide : function(){
45347         this.el.unclip();
45348         this.resizeEl.unclip();
45349     },
45350     
45351     /**
45352      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45353      *   Will fail silently if the {@link #setUrl} method has not been called.
45354      *   This does not activate the panel, just updates its content.
45355      */
45356     refresh : function(){
45357         if(this.refreshDelegate){
45358            this.loaded = false;
45359            this.refreshDelegate();
45360         }
45361     },
45362     
45363     /**
45364      * Destroys this panel
45365      */
45366     destroy : function(){
45367         this.el.removeAllListeners();
45368         var tempEl = document.createElement("span");
45369         tempEl.appendChild(this.el.dom);
45370         tempEl.innerHTML = "";
45371         this.el.remove();
45372         this.el = null;
45373     },
45374     
45375     /**
45376      * form - if the content panel contains a form - this is a reference to it.
45377      * @type {Roo.form.Form}
45378      */
45379     form : false,
45380     /**
45381      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45382      *    This contains a reference to it.
45383      * @type {Roo.View}
45384      */
45385     view : false,
45386     
45387       /**
45388      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45389      * <pre><code>
45390
45391 layout.addxtype({
45392        xtype : 'Form',
45393        items: [ .... ]
45394    }
45395 );
45396
45397 </code></pre>
45398      * @param {Object} cfg Xtype definition of item to add.
45399      */
45400     
45401     
45402     getChildContainer: function () {
45403         return this.getEl();
45404     },
45405     
45406     
45407     onScroll : function(e)
45408     {
45409         this.fireEvent('scroll', this, e);
45410     }
45411     
45412     
45413     /*
45414         var  ret = new Roo.factory(cfg);
45415         return ret;
45416         
45417         
45418         // add form..
45419         if (cfg.xtype.match(/^Form$/)) {
45420             
45421             var el;
45422             //if (this.footer) {
45423             //    el = this.footer.container.insertSibling(false, 'before');
45424             //} else {
45425                 el = this.el.createChild();
45426             //}
45427
45428             this.form = new  Roo.form.Form(cfg);
45429             
45430             
45431             if ( this.form.allItems.length) {
45432                 this.form.render(el.dom);
45433             }
45434             return this.form;
45435         }
45436         // should only have one of theses..
45437         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45438             // views.. should not be just added - used named prop 'view''
45439             
45440             cfg.el = this.el.appendChild(document.createElement("div"));
45441             // factory?
45442             
45443             var ret = new Roo.factory(cfg);
45444              
45445              ret.render && ret.render(false, ''); // render blank..
45446             this.view = ret;
45447             return ret;
45448         }
45449         return false;
45450     }
45451     \*/
45452 });
45453  
45454 /**
45455  * @class Roo.bootstrap.panel.Grid
45456  * @extends Roo.bootstrap.panel.Content
45457  * @constructor
45458  * Create a new GridPanel.
45459  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45460  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45461  * @param {Object} config A the config object
45462   
45463  */
45464
45465
45466
45467 Roo.bootstrap.panel.Grid = function(config)
45468 {
45469     
45470       
45471     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45472         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45473
45474     config.el = this.wrapper;
45475     //this.el = this.wrapper;
45476     
45477       if (config.container) {
45478         // ctor'ed from a Border/panel.grid
45479         
45480         
45481         this.wrapper.setStyle("overflow", "hidden");
45482         this.wrapper.addClass('roo-grid-container');
45483
45484     }
45485     
45486     
45487     if(config.toolbar){
45488         var tool_el = this.wrapper.createChild();    
45489         this.toolbar = Roo.factory(config.toolbar);
45490         var ti = [];
45491         if (config.toolbar.items) {
45492             ti = config.toolbar.items ;
45493             delete config.toolbar.items ;
45494         }
45495         
45496         var nitems = [];
45497         this.toolbar.render(tool_el);
45498         for(var i =0;i < ti.length;i++) {
45499           //  Roo.log(['add child', items[i]]);
45500             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45501         }
45502         this.toolbar.items = nitems;
45503         
45504         delete config.toolbar;
45505     }
45506     
45507     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45508     config.grid.scrollBody = true;;
45509     config.grid.monitorWindowResize = false; // turn off autosizing
45510     config.grid.autoHeight = false;
45511     config.grid.autoWidth = false;
45512     
45513     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45514     
45515     if (config.background) {
45516         // render grid on panel activation (if panel background)
45517         this.on('activate', function(gp) {
45518             if (!gp.grid.rendered) {
45519                 gp.grid.render(this.wrapper);
45520                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45521             }
45522         });
45523             
45524     } else {
45525         this.grid.render(this.wrapper);
45526         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45527
45528     }
45529     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45530     // ??? needed ??? config.el = this.wrapper;
45531     
45532     
45533     
45534   
45535     // xtype created footer. - not sure if will work as we normally have to render first..
45536     if (this.footer && !this.footer.el && this.footer.xtype) {
45537         
45538         var ctr = this.grid.getView().getFooterPanel(true);
45539         this.footer.dataSource = this.grid.dataSource;
45540         this.footer = Roo.factory(this.footer, Roo);
45541         this.footer.render(ctr);
45542         
45543     }
45544     
45545     
45546     
45547     
45548      
45549 };
45550
45551 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45552 {
45553   
45554     getId : function(){
45555         return this.grid.id;
45556     },
45557     
45558     /**
45559      * Returns the grid for this panel
45560      * @return {Roo.bootstrap.Table} 
45561      */
45562     getGrid : function(){
45563         return this.grid;    
45564     },
45565     
45566     setSize : function(width, height)
45567     {
45568      
45569         //if(!this.ignoreResize(width, height)){
45570             var grid = this.grid;
45571             var size = this.adjustForComponents(width, height);
45572             // tfoot is not a footer?
45573           
45574             
45575             var gridel = grid.getGridEl();
45576             gridel.setSize(size.width, size.height);
45577             
45578             var tbd = grid.getGridEl().select('tbody', true).first();
45579             var thd = grid.getGridEl().select('thead',true).first();
45580             var tbf= grid.getGridEl().select('tfoot', true).first();
45581
45582             if (tbf) {
45583                 size.height -= tbf.getHeight();
45584             }
45585             if (thd) {
45586                 size.height -= thd.getHeight();
45587             }
45588             
45589             tbd.setSize(size.width, size.height );
45590             // this is for the account management tab -seems to work there.
45591             var thd = grid.getGridEl().select('thead',true).first();
45592             //if (tbd) {
45593             //    tbd.setSize(size.width, size.height - thd.getHeight());
45594             //}
45595              
45596             grid.autoSize();
45597         //}
45598    
45599     },
45600      
45601     
45602     
45603     beforeSlide : function(){
45604         this.grid.getView().scroller.clip();
45605     },
45606     
45607     afterSlide : function(){
45608         this.grid.getView().scroller.unclip();
45609     },
45610     
45611     destroy : function(){
45612         this.grid.destroy();
45613         delete this.grid;
45614         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45615     }
45616 });
45617
45618 /**
45619  * @class Roo.bootstrap.panel.Nest
45620  * @extends Roo.bootstrap.panel.Content
45621  * @constructor
45622  * Create a new Panel, that can contain a layout.Border.
45623  * 
45624  * 
45625  * @param {String/Object} config A string to set only the title or a config object
45626  */
45627 Roo.bootstrap.panel.Nest = function(config)
45628 {
45629     // construct with only one argument..
45630     /* FIXME - implement nicer consturctors
45631     if (layout.layout) {
45632         config = layout;
45633         layout = config.layout;
45634         delete config.layout;
45635     }
45636     if (layout.xtype && !layout.getEl) {
45637         // then layout needs constructing..
45638         layout = Roo.factory(layout, Roo);
45639     }
45640     */
45641     
45642     config.el =  config.layout.getEl();
45643     
45644     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45645     
45646     config.layout.monitorWindowResize = false; // turn off autosizing
45647     this.layout = config.layout;
45648     this.layout.getEl().addClass("roo-layout-nested-layout");
45649     this.layout.parent = this;
45650     
45651     
45652     
45653     
45654 };
45655
45656 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45657     /**
45658     * @cfg {Roo.BorderLayout} layout The layout for this panel
45659     */
45660     layout : false,
45661
45662     setSize : function(width, height){
45663         if(!this.ignoreResize(width, height)){
45664             var size = this.adjustForComponents(width, height);
45665             var el = this.layout.getEl();
45666             if (size.height < 1) {
45667                 el.setWidth(size.width);   
45668             } else {
45669                 el.setSize(size.width, size.height);
45670             }
45671             var touch = el.dom.offsetWidth;
45672             this.layout.layout();
45673             // ie requires a double layout on the first pass
45674             if(Roo.isIE && !this.initialized){
45675                 this.initialized = true;
45676                 this.layout.layout();
45677             }
45678         }
45679     },
45680     
45681     // activate all subpanels if not currently active..
45682     
45683     setActiveState : function(active){
45684         this.active = active;
45685         this.setActiveClass(active);
45686         
45687         if(!active){
45688             this.fireEvent("deactivate", this);
45689             return;
45690         }
45691         
45692         this.fireEvent("activate", this);
45693         // not sure if this should happen before or after..
45694         if (!this.layout) {
45695             return; // should not happen..
45696         }
45697         var reg = false;
45698         for (var r in this.layout.regions) {
45699             reg = this.layout.getRegion(r);
45700             if (reg.getActivePanel()) {
45701                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45702                 reg.setActivePanel(reg.getActivePanel());
45703                 continue;
45704             }
45705             if (!reg.panels.length) {
45706                 continue;
45707             }
45708             reg.showPanel(reg.getPanel(0));
45709         }
45710         
45711         
45712         
45713         
45714     },
45715     
45716     /**
45717      * Returns the nested BorderLayout for this panel
45718      * @return {Roo.BorderLayout} 
45719      */
45720     getLayout : function(){
45721         return this.layout;
45722     },
45723     
45724      /**
45725      * Adds a xtype elements to the layout of the nested panel
45726      * <pre><code>
45727
45728 panel.addxtype({
45729        xtype : 'ContentPanel',
45730        region: 'west',
45731        items: [ .... ]
45732    }
45733 );
45734
45735 panel.addxtype({
45736         xtype : 'NestedLayoutPanel',
45737         region: 'west',
45738         layout: {
45739            center: { },
45740            west: { }   
45741         },
45742         items : [ ... list of content panels or nested layout panels.. ]
45743    }
45744 );
45745 </code></pre>
45746      * @param {Object} cfg Xtype definition of item to add.
45747      */
45748     addxtype : function(cfg) {
45749         return this.layout.addxtype(cfg);
45750     
45751     }
45752 });/*
45753  * Based on:
45754  * Ext JS Library 1.1.1
45755  * Copyright(c) 2006-2007, Ext JS, LLC.
45756  *
45757  * Originally Released Under LGPL - original licence link has changed is not relivant.
45758  *
45759  * Fork - LGPL
45760  * <script type="text/javascript">
45761  */
45762 /**
45763  * @class Roo.TabPanel
45764  * @extends Roo.util.Observable
45765  * A lightweight tab container.
45766  * <br><br>
45767  * Usage:
45768  * <pre><code>
45769 // basic tabs 1, built from existing content
45770 var tabs = new Roo.TabPanel("tabs1");
45771 tabs.addTab("script", "View Script");
45772 tabs.addTab("markup", "View Markup");
45773 tabs.activate("script");
45774
45775 // more advanced tabs, built from javascript
45776 var jtabs = new Roo.TabPanel("jtabs");
45777 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45778
45779 // set up the UpdateManager
45780 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45781 var updater = tab2.getUpdateManager();
45782 updater.setDefaultUrl("ajax1.htm");
45783 tab2.on('activate', updater.refresh, updater, true);
45784
45785 // Use setUrl for Ajax loading
45786 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45787 tab3.setUrl("ajax2.htm", null, true);
45788
45789 // Disabled tab
45790 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45791 tab4.disable();
45792
45793 jtabs.activate("jtabs-1");
45794  * </code></pre>
45795  * @constructor
45796  * Create a new TabPanel.
45797  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45798  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45799  */
45800 Roo.bootstrap.panel.Tabs = function(config){
45801     /**
45802     * The container element for this TabPanel.
45803     * @type Roo.Element
45804     */
45805     this.el = Roo.get(config.el);
45806     delete config.el;
45807     if(config){
45808         if(typeof config == "boolean"){
45809             this.tabPosition = config ? "bottom" : "top";
45810         }else{
45811             Roo.apply(this, config);
45812         }
45813     }
45814     
45815     if(this.tabPosition == "bottom"){
45816         // if tabs are at the bottom = create the body first.
45817         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45818         this.el.addClass("roo-tabs-bottom");
45819     }
45820     // next create the tabs holders
45821     
45822     if (this.tabPosition == "west"){
45823         
45824         var reg = this.region; // fake it..
45825         while (reg) {
45826             if (!reg.mgr.parent) {
45827                 break;
45828             }
45829             reg = reg.mgr.parent.region;
45830         }
45831         Roo.log("got nest?");
45832         Roo.log(reg);
45833         if (reg.mgr.getRegion('west')) {
45834             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45835             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45836             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45837             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45838             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45839         
45840             
45841         }
45842         
45843         
45844     } else {
45845      
45846         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45847         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45848         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45849         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45850     }
45851     
45852     
45853     if(Roo.isIE){
45854         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45855     }
45856     
45857     // finally - if tabs are at the top, then create the body last..
45858     if(this.tabPosition != "bottom"){
45859         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45860          * @type Roo.Element
45861          */
45862         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45863         this.el.addClass("roo-tabs-top");
45864     }
45865     this.items = [];
45866
45867     this.bodyEl.setStyle("position", "relative");
45868
45869     this.active = null;
45870     this.activateDelegate = this.activate.createDelegate(this);
45871
45872     this.addEvents({
45873         /**
45874          * @event tabchange
45875          * Fires when the active tab changes
45876          * @param {Roo.TabPanel} this
45877          * @param {Roo.TabPanelItem} activePanel The new active tab
45878          */
45879         "tabchange": true,
45880         /**
45881          * @event beforetabchange
45882          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45883          * @param {Roo.TabPanel} this
45884          * @param {Object} e Set cancel to true on this object to cancel the tab change
45885          * @param {Roo.TabPanelItem} tab The tab being changed to
45886          */
45887         "beforetabchange" : true
45888     });
45889
45890     Roo.EventManager.onWindowResize(this.onResize, this);
45891     this.cpad = this.el.getPadding("lr");
45892     this.hiddenCount = 0;
45893
45894
45895     // toolbar on the tabbar support...
45896     if (this.toolbar) {
45897         alert("no toolbar support yet");
45898         this.toolbar  = false;
45899         /*
45900         var tcfg = this.toolbar;
45901         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45902         this.toolbar = new Roo.Toolbar(tcfg);
45903         if (Roo.isSafari) {
45904             var tbl = tcfg.container.child('table', true);
45905             tbl.setAttribute('width', '100%');
45906         }
45907         */
45908         
45909     }
45910    
45911
45912
45913     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45914 };
45915
45916 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45917     /*
45918      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45919      */
45920     tabPosition : "top",
45921     /*
45922      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45923      */
45924     currentTabWidth : 0,
45925     /*
45926      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45927      */
45928     minTabWidth : 40,
45929     /*
45930      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45931      */
45932     maxTabWidth : 250,
45933     /*
45934      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45935      */
45936     preferredTabWidth : 175,
45937     /*
45938      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45939      */
45940     resizeTabs : false,
45941     /*
45942      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45943      */
45944     monitorResize : true,
45945     /*
45946      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45947      */
45948     toolbar : false,  // set by caller..
45949     
45950     region : false, /// set by caller
45951     
45952     disableTooltips : true, // not used yet...
45953
45954     /**
45955      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45956      * @param {String} id The id of the div to use <b>or create</b>
45957      * @param {String} text The text for the tab
45958      * @param {String} content (optional) Content to put in the TabPanelItem body
45959      * @param {Boolean} closable (optional) True to create a close icon on the tab
45960      * @return {Roo.TabPanelItem} The created TabPanelItem
45961      */
45962     addTab : function(id, text, content, closable, tpl)
45963     {
45964         var item = new Roo.bootstrap.panel.TabItem({
45965             panel: this,
45966             id : id,
45967             text : text,
45968             closable : closable,
45969             tpl : tpl
45970         });
45971         this.addTabItem(item);
45972         if(content){
45973             item.setContent(content);
45974         }
45975         return item;
45976     },
45977
45978     /**
45979      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45980      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45981      * @return {Roo.TabPanelItem}
45982      */
45983     getTab : function(id){
45984         return this.items[id];
45985     },
45986
45987     /**
45988      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45989      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45990      */
45991     hideTab : function(id){
45992         var t = this.items[id];
45993         if(!t.isHidden()){
45994            t.setHidden(true);
45995            this.hiddenCount++;
45996            this.autoSizeTabs();
45997         }
45998     },
45999
46000     /**
46001      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46002      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46003      */
46004     unhideTab : function(id){
46005         var t = this.items[id];
46006         if(t.isHidden()){
46007            t.setHidden(false);
46008            this.hiddenCount--;
46009            this.autoSizeTabs();
46010         }
46011     },
46012
46013     /**
46014      * Adds an existing {@link Roo.TabPanelItem}.
46015      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46016      */
46017     addTabItem : function(item)
46018     {
46019         this.items[item.id] = item;
46020         this.items.push(item);
46021         this.autoSizeTabs();
46022       //  if(this.resizeTabs){
46023     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46024   //         this.autoSizeTabs();
46025 //        }else{
46026 //            item.autoSize();
46027        // }
46028     },
46029
46030     /**
46031      * Removes a {@link Roo.TabPanelItem}.
46032      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46033      */
46034     removeTab : function(id){
46035         var items = this.items;
46036         var tab = items[id];
46037         if(!tab) { return; }
46038         var index = items.indexOf(tab);
46039         if(this.active == tab && items.length > 1){
46040             var newTab = this.getNextAvailable(index);
46041             if(newTab) {
46042                 newTab.activate();
46043             }
46044         }
46045         this.stripEl.dom.removeChild(tab.pnode.dom);
46046         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46047             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46048         }
46049         items.splice(index, 1);
46050         delete this.items[tab.id];
46051         tab.fireEvent("close", tab);
46052         tab.purgeListeners();
46053         this.autoSizeTabs();
46054     },
46055
46056     getNextAvailable : function(start){
46057         var items = this.items;
46058         var index = start;
46059         // look for a next tab that will slide over to
46060         // replace the one being removed
46061         while(index < items.length){
46062             var item = items[++index];
46063             if(item && !item.isHidden()){
46064                 return item;
46065             }
46066         }
46067         // if one isn't found select the previous tab (on the left)
46068         index = start;
46069         while(index >= 0){
46070             var item = items[--index];
46071             if(item && !item.isHidden()){
46072                 return item;
46073             }
46074         }
46075         return null;
46076     },
46077
46078     /**
46079      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46080      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46081      */
46082     disableTab : function(id){
46083         var tab = this.items[id];
46084         if(tab && this.active != tab){
46085             tab.disable();
46086         }
46087     },
46088
46089     /**
46090      * Enables a {@link Roo.TabPanelItem} that is disabled.
46091      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46092      */
46093     enableTab : function(id){
46094         var tab = this.items[id];
46095         tab.enable();
46096     },
46097
46098     /**
46099      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46100      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46101      * @return {Roo.TabPanelItem} The TabPanelItem.
46102      */
46103     activate : function(id)
46104     {
46105         //Roo.log('activite:'  + id);
46106         
46107         var tab = this.items[id];
46108         if(!tab){
46109             return null;
46110         }
46111         if(tab == this.active || tab.disabled){
46112             return tab;
46113         }
46114         var e = {};
46115         this.fireEvent("beforetabchange", this, e, tab);
46116         if(e.cancel !== true && !tab.disabled){
46117             if(this.active){
46118                 this.active.hide();
46119             }
46120             this.active = this.items[id];
46121             this.active.show();
46122             this.fireEvent("tabchange", this, this.active);
46123         }
46124         return tab;
46125     },
46126
46127     /**
46128      * Gets the active {@link Roo.TabPanelItem}.
46129      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46130      */
46131     getActiveTab : function(){
46132         return this.active;
46133     },
46134
46135     /**
46136      * Updates the tab body element to fit the height of the container element
46137      * for overflow scrolling
46138      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46139      */
46140     syncHeight : function(targetHeight){
46141         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46142         var bm = this.bodyEl.getMargins();
46143         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46144         this.bodyEl.setHeight(newHeight);
46145         return newHeight;
46146     },
46147
46148     onResize : function(){
46149         if(this.monitorResize){
46150             this.autoSizeTabs();
46151         }
46152     },
46153
46154     /**
46155      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46156      */
46157     beginUpdate : function(){
46158         this.updating = true;
46159     },
46160
46161     /**
46162      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46163      */
46164     endUpdate : function(){
46165         this.updating = false;
46166         this.autoSizeTabs();
46167     },
46168
46169     /**
46170      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46171      */
46172     autoSizeTabs : function()
46173     {
46174         var count = this.items.length;
46175         var vcount = count - this.hiddenCount;
46176         
46177         if (vcount < 2) {
46178             this.stripEl.hide();
46179         } else {
46180             this.stripEl.show();
46181         }
46182         
46183         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46184             return;
46185         }
46186         
46187         
46188         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46189         var availWidth = Math.floor(w / vcount);
46190         var b = this.stripBody;
46191         if(b.getWidth() > w){
46192             var tabs = this.items;
46193             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46194             if(availWidth < this.minTabWidth){
46195                 /*if(!this.sleft){    // incomplete scrolling code
46196                     this.createScrollButtons();
46197                 }
46198                 this.showScroll();
46199                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46200             }
46201         }else{
46202             if(this.currentTabWidth < this.preferredTabWidth){
46203                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46204             }
46205         }
46206     },
46207
46208     /**
46209      * Returns the number of tabs in this TabPanel.
46210      * @return {Number}
46211      */
46212      getCount : function(){
46213          return this.items.length;
46214      },
46215
46216     /**
46217      * Resizes all the tabs to the passed width
46218      * @param {Number} The new width
46219      */
46220     setTabWidth : function(width){
46221         this.currentTabWidth = width;
46222         for(var i = 0, len = this.items.length; i < len; i++) {
46223                 if(!this.items[i].isHidden()) {
46224                 this.items[i].setWidth(width);
46225             }
46226         }
46227     },
46228
46229     /**
46230      * Destroys this TabPanel
46231      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46232      */
46233     destroy : function(removeEl){
46234         Roo.EventManager.removeResizeListener(this.onResize, this);
46235         for(var i = 0, len = this.items.length; i < len; i++){
46236             this.items[i].purgeListeners();
46237         }
46238         if(removeEl === true){
46239             this.el.update("");
46240             this.el.remove();
46241         }
46242     },
46243     
46244     createStrip : function(container)
46245     {
46246         var strip = document.createElement("nav");
46247         strip.className = Roo.bootstrap.version == 4 ?
46248             "navbar-light bg-light" : 
46249             "navbar navbar-default"; //"x-tabs-wrap";
46250         container.appendChild(strip);
46251         return strip;
46252     },
46253     
46254     createStripList : function(strip)
46255     {
46256         // div wrapper for retard IE
46257         // returns the "tr" element.
46258         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46259         //'<div class="x-tabs-strip-wrap">'+
46260           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46261           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46262         return strip.firstChild; //.firstChild.firstChild.firstChild;
46263     },
46264     createBody : function(container)
46265     {
46266         var body = document.createElement("div");
46267         Roo.id(body, "tab-body");
46268         //Roo.fly(body).addClass("x-tabs-body");
46269         Roo.fly(body).addClass("tab-content");
46270         container.appendChild(body);
46271         return body;
46272     },
46273     createItemBody :function(bodyEl, id){
46274         var body = Roo.getDom(id);
46275         if(!body){
46276             body = document.createElement("div");
46277             body.id = id;
46278         }
46279         //Roo.fly(body).addClass("x-tabs-item-body");
46280         Roo.fly(body).addClass("tab-pane");
46281          bodyEl.insertBefore(body, bodyEl.firstChild);
46282         return body;
46283     },
46284     /** @private */
46285     createStripElements :  function(stripEl, text, closable, tpl)
46286     {
46287         var td = document.createElement("li"); // was td..
46288         td.className = 'nav-item';
46289         
46290         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46291         
46292         
46293         stripEl.appendChild(td);
46294         /*if(closable){
46295             td.className = "x-tabs-closable";
46296             if(!this.closeTpl){
46297                 this.closeTpl = new Roo.Template(
46298                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46299                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46300                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46301                 );
46302             }
46303             var el = this.closeTpl.overwrite(td, {"text": text});
46304             var close = el.getElementsByTagName("div")[0];
46305             var inner = el.getElementsByTagName("em")[0];
46306             return {"el": el, "close": close, "inner": inner};
46307         } else {
46308         */
46309         // not sure what this is..
46310 //            if(!this.tabTpl){
46311                 //this.tabTpl = new Roo.Template(
46312                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46313                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46314                 //);
46315 //                this.tabTpl = new Roo.Template(
46316 //                   '<a href="#">' +
46317 //                   '<span unselectable="on"' +
46318 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46319 //                            ' >{text}</span></a>'
46320 //                );
46321 //                
46322 //            }
46323
46324
46325             var template = tpl || this.tabTpl || false;
46326             
46327             if(!template){
46328                 template =  new Roo.Template(
46329                         Roo.bootstrap.version == 4 ? 
46330                             (
46331                                 '<a class="nav-link" href="#" unselectable="on"' +
46332                                      (this.disableTooltips ? '' : ' title="{text}"') +
46333                                      ' >{text}</a>'
46334                             ) : (
46335                                 '<a class="nav-link" href="#">' +
46336                                 '<span unselectable="on"' +
46337                                          (this.disableTooltips ? '' : ' title="{text}"') +
46338                                     ' >{text}</span></a>'
46339                             )
46340                 );
46341             }
46342             
46343             switch (typeof(template)) {
46344                 case 'object' :
46345                     break;
46346                 case 'string' :
46347                     template = new Roo.Template(template);
46348                     break;
46349                 default :
46350                     break;
46351             }
46352             
46353             var el = template.overwrite(td, {"text": text});
46354             
46355             var inner = el.getElementsByTagName("span")[0];
46356             
46357             return {"el": el, "inner": inner};
46358             
46359     }
46360         
46361     
46362 });
46363
46364 /**
46365  * @class Roo.TabPanelItem
46366  * @extends Roo.util.Observable
46367  * Represents an individual item (tab plus body) in a TabPanel.
46368  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46369  * @param {String} id The id of this TabPanelItem
46370  * @param {String} text The text for the tab of this TabPanelItem
46371  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46372  */
46373 Roo.bootstrap.panel.TabItem = function(config){
46374     /**
46375      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46376      * @type Roo.TabPanel
46377      */
46378     this.tabPanel = config.panel;
46379     /**
46380      * The id for this TabPanelItem
46381      * @type String
46382      */
46383     this.id = config.id;
46384     /** @private */
46385     this.disabled = false;
46386     /** @private */
46387     this.text = config.text;
46388     /** @private */
46389     this.loaded = false;
46390     this.closable = config.closable;
46391
46392     /**
46393      * The body element for this TabPanelItem.
46394      * @type Roo.Element
46395      */
46396     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46397     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46398     this.bodyEl.setStyle("display", "block");
46399     this.bodyEl.setStyle("zoom", "1");
46400     //this.hideAction();
46401
46402     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46403     /** @private */
46404     this.el = Roo.get(els.el);
46405     this.inner = Roo.get(els.inner, true);
46406      this.textEl = Roo.bootstrap.version == 4 ?
46407         this.el : Roo.get(this.el.dom.firstChild, true);
46408
46409     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46410     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46411
46412     
46413 //    this.el.on("mousedown", this.onTabMouseDown, this);
46414     this.el.on("click", this.onTabClick, this);
46415     /** @private */
46416     if(config.closable){
46417         var c = Roo.get(els.close, true);
46418         c.dom.title = this.closeText;
46419         c.addClassOnOver("close-over");
46420         c.on("click", this.closeClick, this);
46421      }
46422
46423     this.addEvents({
46424          /**
46425          * @event activate
46426          * Fires when this tab becomes the active tab.
46427          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46428          * @param {Roo.TabPanelItem} this
46429          */
46430         "activate": true,
46431         /**
46432          * @event beforeclose
46433          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46434          * @param {Roo.TabPanelItem} this
46435          * @param {Object} e Set cancel to true on this object to cancel the close.
46436          */
46437         "beforeclose": true,
46438         /**
46439          * @event close
46440          * Fires when this tab is closed.
46441          * @param {Roo.TabPanelItem} this
46442          */
46443          "close": true,
46444         /**
46445          * @event deactivate
46446          * Fires when this tab is no longer the active tab.
46447          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46448          * @param {Roo.TabPanelItem} this
46449          */
46450          "deactivate" : true
46451     });
46452     this.hidden = false;
46453
46454     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46455 };
46456
46457 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46458            {
46459     purgeListeners : function(){
46460        Roo.util.Observable.prototype.purgeListeners.call(this);
46461        this.el.removeAllListeners();
46462     },
46463     /**
46464      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46465      */
46466     show : function(){
46467         this.status_node.addClass("active");
46468         this.showAction();
46469         if(Roo.isOpera){
46470             this.tabPanel.stripWrap.repaint();
46471         }
46472         this.fireEvent("activate", this.tabPanel, this);
46473     },
46474
46475     /**
46476      * Returns true if this tab is the active tab.
46477      * @return {Boolean}
46478      */
46479     isActive : function(){
46480         return this.tabPanel.getActiveTab() == this;
46481     },
46482
46483     /**
46484      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46485      */
46486     hide : function(){
46487         this.status_node.removeClass("active");
46488         this.hideAction();
46489         this.fireEvent("deactivate", this.tabPanel, this);
46490     },
46491
46492     hideAction : function(){
46493         this.bodyEl.hide();
46494         this.bodyEl.setStyle("position", "absolute");
46495         this.bodyEl.setLeft("-20000px");
46496         this.bodyEl.setTop("-20000px");
46497     },
46498
46499     showAction : function(){
46500         this.bodyEl.setStyle("position", "relative");
46501         this.bodyEl.setTop("");
46502         this.bodyEl.setLeft("");
46503         this.bodyEl.show();
46504     },
46505
46506     /**
46507      * Set the tooltip for the tab.
46508      * @param {String} tooltip The tab's tooltip
46509      */
46510     setTooltip : function(text){
46511         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46512             this.textEl.dom.qtip = text;
46513             this.textEl.dom.removeAttribute('title');
46514         }else{
46515             this.textEl.dom.title = text;
46516         }
46517     },
46518
46519     onTabClick : function(e){
46520         e.preventDefault();
46521         this.tabPanel.activate(this.id);
46522     },
46523
46524     onTabMouseDown : function(e){
46525         e.preventDefault();
46526         this.tabPanel.activate(this.id);
46527     },
46528 /*
46529     getWidth : function(){
46530         return this.inner.getWidth();
46531     },
46532
46533     setWidth : function(width){
46534         var iwidth = width - this.linode.getPadding("lr");
46535         this.inner.setWidth(iwidth);
46536         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46537         this.linode.setWidth(width);
46538     },
46539 */
46540     /**
46541      * Show or hide the tab
46542      * @param {Boolean} hidden True to hide or false to show.
46543      */
46544     setHidden : function(hidden){
46545         this.hidden = hidden;
46546         this.linode.setStyle("display", hidden ? "none" : "");
46547     },
46548
46549     /**
46550      * Returns true if this tab is "hidden"
46551      * @return {Boolean}
46552      */
46553     isHidden : function(){
46554         return this.hidden;
46555     },
46556
46557     /**
46558      * Returns the text for this tab
46559      * @return {String}
46560      */
46561     getText : function(){
46562         return this.text;
46563     },
46564     /*
46565     autoSize : function(){
46566         //this.el.beginMeasure();
46567         this.textEl.setWidth(1);
46568         /*
46569          *  #2804 [new] Tabs in Roojs
46570          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46571          */
46572         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46573         //this.el.endMeasure();
46574     //},
46575
46576     /**
46577      * Sets the text for the tab (Note: this also sets the tooltip text)
46578      * @param {String} text The tab's text and tooltip
46579      */
46580     setText : function(text){
46581         this.text = text;
46582         this.textEl.update(text);
46583         this.setTooltip(text);
46584         //if(!this.tabPanel.resizeTabs){
46585         //    this.autoSize();
46586         //}
46587     },
46588     /**
46589      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46590      */
46591     activate : function(){
46592         this.tabPanel.activate(this.id);
46593     },
46594
46595     /**
46596      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46597      */
46598     disable : function(){
46599         if(this.tabPanel.active != this){
46600             this.disabled = true;
46601             this.status_node.addClass("disabled");
46602         }
46603     },
46604
46605     /**
46606      * Enables this TabPanelItem if it was previously disabled.
46607      */
46608     enable : function(){
46609         this.disabled = false;
46610         this.status_node.removeClass("disabled");
46611     },
46612
46613     /**
46614      * Sets the content for this TabPanelItem.
46615      * @param {String} content The content
46616      * @param {Boolean} loadScripts true to look for and load scripts
46617      */
46618     setContent : function(content, loadScripts){
46619         this.bodyEl.update(content, loadScripts);
46620     },
46621
46622     /**
46623      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46624      * @return {Roo.UpdateManager} The UpdateManager
46625      */
46626     getUpdateManager : function(){
46627         return this.bodyEl.getUpdateManager();
46628     },
46629
46630     /**
46631      * Set a URL to be used to load the content for this TabPanelItem.
46632      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46633      * @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)
46634      * @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)
46635      * @return {Roo.UpdateManager} The UpdateManager
46636      */
46637     setUrl : function(url, params, loadOnce){
46638         if(this.refreshDelegate){
46639             this.un('activate', this.refreshDelegate);
46640         }
46641         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46642         this.on("activate", this.refreshDelegate);
46643         return this.bodyEl.getUpdateManager();
46644     },
46645
46646     /** @private */
46647     _handleRefresh : function(url, params, loadOnce){
46648         if(!loadOnce || !this.loaded){
46649             var updater = this.bodyEl.getUpdateManager();
46650             updater.update(url, params, this._setLoaded.createDelegate(this));
46651         }
46652     },
46653
46654     /**
46655      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46656      *   Will fail silently if the setUrl method has not been called.
46657      *   This does not activate the panel, just updates its content.
46658      */
46659     refresh : function(){
46660         if(this.refreshDelegate){
46661            this.loaded = false;
46662            this.refreshDelegate();
46663         }
46664     },
46665
46666     /** @private */
46667     _setLoaded : function(){
46668         this.loaded = true;
46669     },
46670
46671     /** @private */
46672     closeClick : function(e){
46673         var o = {};
46674         e.stopEvent();
46675         this.fireEvent("beforeclose", this, o);
46676         if(o.cancel !== true){
46677             this.tabPanel.removeTab(this.id);
46678         }
46679     },
46680     /**
46681      * The text displayed in the tooltip for the close icon.
46682      * @type String
46683      */
46684     closeText : "Close this tab"
46685 });
46686 /**
46687 *    This script refer to:
46688 *    Title: International Telephone Input
46689 *    Author: Jack O'Connor
46690 *    Code version:  v12.1.12
46691 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46692 **/
46693
46694 Roo.bootstrap.form.PhoneInputData = function() {
46695     var d = [
46696       [
46697         "Afghanistan (‫افغانستان‬‎)",
46698         "af",
46699         "93"
46700       ],
46701       [
46702         "Albania (Shqipëri)",
46703         "al",
46704         "355"
46705       ],
46706       [
46707         "Algeria (‫الجزائر‬‎)",
46708         "dz",
46709         "213"
46710       ],
46711       [
46712         "American Samoa",
46713         "as",
46714         "1684"
46715       ],
46716       [
46717         "Andorra",
46718         "ad",
46719         "376"
46720       ],
46721       [
46722         "Angola",
46723         "ao",
46724         "244"
46725       ],
46726       [
46727         "Anguilla",
46728         "ai",
46729         "1264"
46730       ],
46731       [
46732         "Antigua and Barbuda",
46733         "ag",
46734         "1268"
46735       ],
46736       [
46737         "Argentina",
46738         "ar",
46739         "54"
46740       ],
46741       [
46742         "Armenia (Հայաստան)",
46743         "am",
46744         "374"
46745       ],
46746       [
46747         "Aruba",
46748         "aw",
46749         "297"
46750       ],
46751       [
46752         "Australia",
46753         "au",
46754         "61",
46755         0
46756       ],
46757       [
46758         "Austria (Österreich)",
46759         "at",
46760         "43"
46761       ],
46762       [
46763         "Azerbaijan (Azərbaycan)",
46764         "az",
46765         "994"
46766       ],
46767       [
46768         "Bahamas",
46769         "bs",
46770         "1242"
46771       ],
46772       [
46773         "Bahrain (‫البحرين‬‎)",
46774         "bh",
46775         "973"
46776       ],
46777       [
46778         "Bangladesh (বাংলাদেশ)",
46779         "bd",
46780         "880"
46781       ],
46782       [
46783         "Barbados",
46784         "bb",
46785         "1246"
46786       ],
46787       [
46788         "Belarus (Беларусь)",
46789         "by",
46790         "375"
46791       ],
46792       [
46793         "Belgium (België)",
46794         "be",
46795         "32"
46796       ],
46797       [
46798         "Belize",
46799         "bz",
46800         "501"
46801       ],
46802       [
46803         "Benin (Bénin)",
46804         "bj",
46805         "229"
46806       ],
46807       [
46808         "Bermuda",
46809         "bm",
46810         "1441"
46811       ],
46812       [
46813         "Bhutan (འབྲུག)",
46814         "bt",
46815         "975"
46816       ],
46817       [
46818         "Bolivia",
46819         "bo",
46820         "591"
46821       ],
46822       [
46823         "Bosnia and Herzegovina (Босна и Херцеговина)",
46824         "ba",
46825         "387"
46826       ],
46827       [
46828         "Botswana",
46829         "bw",
46830         "267"
46831       ],
46832       [
46833         "Brazil (Brasil)",
46834         "br",
46835         "55"
46836       ],
46837       [
46838         "British Indian Ocean Territory",
46839         "io",
46840         "246"
46841       ],
46842       [
46843         "British Virgin Islands",
46844         "vg",
46845         "1284"
46846       ],
46847       [
46848         "Brunei",
46849         "bn",
46850         "673"
46851       ],
46852       [
46853         "Bulgaria (България)",
46854         "bg",
46855         "359"
46856       ],
46857       [
46858         "Burkina Faso",
46859         "bf",
46860         "226"
46861       ],
46862       [
46863         "Burundi (Uburundi)",
46864         "bi",
46865         "257"
46866       ],
46867       [
46868         "Cambodia (កម្ពុជា)",
46869         "kh",
46870         "855"
46871       ],
46872       [
46873         "Cameroon (Cameroun)",
46874         "cm",
46875         "237"
46876       ],
46877       [
46878         "Canada",
46879         "ca",
46880         "1",
46881         1,
46882         ["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"]
46883       ],
46884       [
46885         "Cape Verde (Kabu Verdi)",
46886         "cv",
46887         "238"
46888       ],
46889       [
46890         "Caribbean Netherlands",
46891         "bq",
46892         "599",
46893         1
46894       ],
46895       [
46896         "Cayman Islands",
46897         "ky",
46898         "1345"
46899       ],
46900       [
46901         "Central African Republic (République centrafricaine)",
46902         "cf",
46903         "236"
46904       ],
46905       [
46906         "Chad (Tchad)",
46907         "td",
46908         "235"
46909       ],
46910       [
46911         "Chile",
46912         "cl",
46913         "56"
46914       ],
46915       [
46916         "China (中国)",
46917         "cn",
46918         "86"
46919       ],
46920       [
46921         "Christmas Island",
46922         "cx",
46923         "61",
46924         2
46925       ],
46926       [
46927         "Cocos (Keeling) Islands",
46928         "cc",
46929         "61",
46930         1
46931       ],
46932       [
46933         "Colombia",
46934         "co",
46935         "57"
46936       ],
46937       [
46938         "Comoros (‫جزر القمر‬‎)",
46939         "km",
46940         "269"
46941       ],
46942       [
46943         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46944         "cd",
46945         "243"
46946       ],
46947       [
46948         "Congo (Republic) (Congo-Brazzaville)",
46949         "cg",
46950         "242"
46951       ],
46952       [
46953         "Cook Islands",
46954         "ck",
46955         "682"
46956       ],
46957       [
46958         "Costa Rica",
46959         "cr",
46960         "506"
46961       ],
46962       [
46963         "Côte d’Ivoire",
46964         "ci",
46965         "225"
46966       ],
46967       [
46968         "Croatia (Hrvatska)",
46969         "hr",
46970         "385"
46971       ],
46972       [
46973         "Cuba",
46974         "cu",
46975         "53"
46976       ],
46977       [
46978         "Curaçao",
46979         "cw",
46980         "599",
46981         0
46982       ],
46983       [
46984         "Cyprus (Κύπρος)",
46985         "cy",
46986         "357"
46987       ],
46988       [
46989         "Czech Republic (Česká republika)",
46990         "cz",
46991         "420"
46992       ],
46993       [
46994         "Denmark (Danmark)",
46995         "dk",
46996         "45"
46997       ],
46998       [
46999         "Djibouti",
47000         "dj",
47001         "253"
47002       ],
47003       [
47004         "Dominica",
47005         "dm",
47006         "1767"
47007       ],
47008       [
47009         "Dominican Republic (República Dominicana)",
47010         "do",
47011         "1",
47012         2,
47013         ["809", "829", "849"]
47014       ],
47015       [
47016         "Ecuador",
47017         "ec",
47018         "593"
47019       ],
47020       [
47021         "Egypt (‫مصر‬‎)",
47022         "eg",
47023         "20"
47024       ],
47025       [
47026         "El Salvador",
47027         "sv",
47028         "503"
47029       ],
47030       [
47031         "Equatorial Guinea (Guinea Ecuatorial)",
47032         "gq",
47033         "240"
47034       ],
47035       [
47036         "Eritrea",
47037         "er",
47038         "291"
47039       ],
47040       [
47041         "Estonia (Eesti)",
47042         "ee",
47043         "372"
47044       ],
47045       [
47046         "Ethiopia",
47047         "et",
47048         "251"
47049       ],
47050       [
47051         "Falkland Islands (Islas Malvinas)",
47052         "fk",
47053         "500"
47054       ],
47055       [
47056         "Faroe Islands (Føroyar)",
47057         "fo",
47058         "298"
47059       ],
47060       [
47061         "Fiji",
47062         "fj",
47063         "679"
47064       ],
47065       [
47066         "Finland (Suomi)",
47067         "fi",
47068         "358",
47069         0
47070       ],
47071       [
47072         "France",
47073         "fr",
47074         "33"
47075       ],
47076       [
47077         "French Guiana (Guyane française)",
47078         "gf",
47079         "594"
47080       ],
47081       [
47082         "French Polynesia (Polynésie française)",
47083         "pf",
47084         "689"
47085       ],
47086       [
47087         "Gabon",
47088         "ga",
47089         "241"
47090       ],
47091       [
47092         "Gambia",
47093         "gm",
47094         "220"
47095       ],
47096       [
47097         "Georgia (საქართველო)",
47098         "ge",
47099         "995"
47100       ],
47101       [
47102         "Germany (Deutschland)",
47103         "de",
47104         "49"
47105       ],
47106       [
47107         "Ghana (Gaana)",
47108         "gh",
47109         "233"
47110       ],
47111       [
47112         "Gibraltar",
47113         "gi",
47114         "350"
47115       ],
47116       [
47117         "Greece (Ελλάδα)",
47118         "gr",
47119         "30"
47120       ],
47121       [
47122         "Greenland (Kalaallit Nunaat)",
47123         "gl",
47124         "299"
47125       ],
47126       [
47127         "Grenada",
47128         "gd",
47129         "1473"
47130       ],
47131       [
47132         "Guadeloupe",
47133         "gp",
47134         "590",
47135         0
47136       ],
47137       [
47138         "Guam",
47139         "gu",
47140         "1671"
47141       ],
47142       [
47143         "Guatemala",
47144         "gt",
47145         "502"
47146       ],
47147       [
47148         "Guernsey",
47149         "gg",
47150         "44",
47151         1
47152       ],
47153       [
47154         "Guinea (Guinée)",
47155         "gn",
47156         "224"
47157       ],
47158       [
47159         "Guinea-Bissau (Guiné Bissau)",
47160         "gw",
47161         "245"
47162       ],
47163       [
47164         "Guyana",
47165         "gy",
47166         "592"
47167       ],
47168       [
47169         "Haiti",
47170         "ht",
47171         "509"
47172       ],
47173       [
47174         "Honduras",
47175         "hn",
47176         "504"
47177       ],
47178       [
47179         "Hong Kong (香港)",
47180         "hk",
47181         "852"
47182       ],
47183       [
47184         "Hungary (Magyarország)",
47185         "hu",
47186         "36"
47187       ],
47188       [
47189         "Iceland (Ísland)",
47190         "is",
47191         "354"
47192       ],
47193       [
47194         "India (भारत)",
47195         "in",
47196         "91"
47197       ],
47198       [
47199         "Indonesia",
47200         "id",
47201         "62"
47202       ],
47203       [
47204         "Iran (‫ایران‬‎)",
47205         "ir",
47206         "98"
47207       ],
47208       [
47209         "Iraq (‫العراق‬‎)",
47210         "iq",
47211         "964"
47212       ],
47213       [
47214         "Ireland",
47215         "ie",
47216         "353"
47217       ],
47218       [
47219         "Isle of Man",
47220         "im",
47221         "44",
47222         2
47223       ],
47224       [
47225         "Israel (‫ישראל‬‎)",
47226         "il",
47227         "972"
47228       ],
47229       [
47230         "Italy (Italia)",
47231         "it",
47232         "39",
47233         0
47234       ],
47235       [
47236         "Jamaica",
47237         "jm",
47238         "1876"
47239       ],
47240       [
47241         "Japan (日本)",
47242         "jp",
47243         "81"
47244       ],
47245       [
47246         "Jersey",
47247         "je",
47248         "44",
47249         3
47250       ],
47251       [
47252         "Jordan (‫الأردن‬‎)",
47253         "jo",
47254         "962"
47255       ],
47256       [
47257         "Kazakhstan (Казахстан)",
47258         "kz",
47259         "7",
47260         1
47261       ],
47262       [
47263         "Kenya",
47264         "ke",
47265         "254"
47266       ],
47267       [
47268         "Kiribati",
47269         "ki",
47270         "686"
47271       ],
47272       [
47273         "Kosovo",
47274         "xk",
47275         "383"
47276       ],
47277       [
47278         "Kuwait (‫الكويت‬‎)",
47279         "kw",
47280         "965"
47281       ],
47282       [
47283         "Kyrgyzstan (Кыргызстан)",
47284         "kg",
47285         "996"
47286       ],
47287       [
47288         "Laos (ລາວ)",
47289         "la",
47290         "856"
47291       ],
47292       [
47293         "Latvia (Latvija)",
47294         "lv",
47295         "371"
47296       ],
47297       [
47298         "Lebanon (‫لبنان‬‎)",
47299         "lb",
47300         "961"
47301       ],
47302       [
47303         "Lesotho",
47304         "ls",
47305         "266"
47306       ],
47307       [
47308         "Liberia",
47309         "lr",
47310         "231"
47311       ],
47312       [
47313         "Libya (‫ليبيا‬‎)",
47314         "ly",
47315         "218"
47316       ],
47317       [
47318         "Liechtenstein",
47319         "li",
47320         "423"
47321       ],
47322       [
47323         "Lithuania (Lietuva)",
47324         "lt",
47325         "370"
47326       ],
47327       [
47328         "Luxembourg",
47329         "lu",
47330         "352"
47331       ],
47332       [
47333         "Macau (澳門)",
47334         "mo",
47335         "853"
47336       ],
47337       [
47338         "Macedonia (FYROM) (Македонија)",
47339         "mk",
47340         "389"
47341       ],
47342       [
47343         "Madagascar (Madagasikara)",
47344         "mg",
47345         "261"
47346       ],
47347       [
47348         "Malawi",
47349         "mw",
47350         "265"
47351       ],
47352       [
47353         "Malaysia",
47354         "my",
47355         "60"
47356       ],
47357       [
47358         "Maldives",
47359         "mv",
47360         "960"
47361       ],
47362       [
47363         "Mali",
47364         "ml",
47365         "223"
47366       ],
47367       [
47368         "Malta",
47369         "mt",
47370         "356"
47371       ],
47372       [
47373         "Marshall Islands",
47374         "mh",
47375         "692"
47376       ],
47377       [
47378         "Martinique",
47379         "mq",
47380         "596"
47381       ],
47382       [
47383         "Mauritania (‫موريتانيا‬‎)",
47384         "mr",
47385         "222"
47386       ],
47387       [
47388         "Mauritius (Moris)",
47389         "mu",
47390         "230"
47391       ],
47392       [
47393         "Mayotte",
47394         "yt",
47395         "262",
47396         1
47397       ],
47398       [
47399         "Mexico (México)",
47400         "mx",
47401         "52"
47402       ],
47403       [
47404         "Micronesia",
47405         "fm",
47406         "691"
47407       ],
47408       [
47409         "Moldova (Republica Moldova)",
47410         "md",
47411         "373"
47412       ],
47413       [
47414         "Monaco",
47415         "mc",
47416         "377"
47417       ],
47418       [
47419         "Mongolia (Монгол)",
47420         "mn",
47421         "976"
47422       ],
47423       [
47424         "Montenegro (Crna Gora)",
47425         "me",
47426         "382"
47427       ],
47428       [
47429         "Montserrat",
47430         "ms",
47431         "1664"
47432       ],
47433       [
47434         "Morocco (‫المغرب‬‎)",
47435         "ma",
47436         "212",
47437         0
47438       ],
47439       [
47440         "Mozambique (Moçambique)",
47441         "mz",
47442         "258"
47443       ],
47444       [
47445         "Myanmar (Burma) (မြန်မာ)",
47446         "mm",
47447         "95"
47448       ],
47449       [
47450         "Namibia (Namibië)",
47451         "na",
47452         "264"
47453       ],
47454       [
47455         "Nauru",
47456         "nr",
47457         "674"
47458       ],
47459       [
47460         "Nepal (नेपाल)",
47461         "np",
47462         "977"
47463       ],
47464       [
47465         "Netherlands (Nederland)",
47466         "nl",
47467         "31"
47468       ],
47469       [
47470         "New Caledonia (Nouvelle-Calédonie)",
47471         "nc",
47472         "687"
47473       ],
47474       [
47475         "New Zealand",
47476         "nz",
47477         "64"
47478       ],
47479       [
47480         "Nicaragua",
47481         "ni",
47482         "505"
47483       ],
47484       [
47485         "Niger (Nijar)",
47486         "ne",
47487         "227"
47488       ],
47489       [
47490         "Nigeria",
47491         "ng",
47492         "234"
47493       ],
47494       [
47495         "Niue",
47496         "nu",
47497         "683"
47498       ],
47499       [
47500         "Norfolk Island",
47501         "nf",
47502         "672"
47503       ],
47504       [
47505         "North Korea (조선 민주주의 인민 공화국)",
47506         "kp",
47507         "850"
47508       ],
47509       [
47510         "Northern Mariana Islands",
47511         "mp",
47512         "1670"
47513       ],
47514       [
47515         "Norway (Norge)",
47516         "no",
47517         "47",
47518         0
47519       ],
47520       [
47521         "Oman (‫عُمان‬‎)",
47522         "om",
47523         "968"
47524       ],
47525       [
47526         "Pakistan (‫پاکستان‬‎)",
47527         "pk",
47528         "92"
47529       ],
47530       [
47531         "Palau",
47532         "pw",
47533         "680"
47534       ],
47535       [
47536         "Palestine (‫فلسطين‬‎)",
47537         "ps",
47538         "970"
47539       ],
47540       [
47541         "Panama (Panamá)",
47542         "pa",
47543         "507"
47544       ],
47545       [
47546         "Papua New Guinea",
47547         "pg",
47548         "675"
47549       ],
47550       [
47551         "Paraguay",
47552         "py",
47553         "595"
47554       ],
47555       [
47556         "Peru (Perú)",
47557         "pe",
47558         "51"
47559       ],
47560       [
47561         "Philippines",
47562         "ph",
47563         "63"
47564       ],
47565       [
47566         "Poland (Polska)",
47567         "pl",
47568         "48"
47569       ],
47570       [
47571         "Portugal",
47572         "pt",
47573         "351"
47574       ],
47575       [
47576         "Puerto Rico",
47577         "pr",
47578         "1",
47579         3,
47580         ["787", "939"]
47581       ],
47582       [
47583         "Qatar (‫قطر‬‎)",
47584         "qa",
47585         "974"
47586       ],
47587       [
47588         "Réunion (La Réunion)",
47589         "re",
47590         "262",
47591         0
47592       ],
47593       [
47594         "Romania (România)",
47595         "ro",
47596         "40"
47597       ],
47598       [
47599         "Russia (Россия)",
47600         "ru",
47601         "7",
47602         0
47603       ],
47604       [
47605         "Rwanda",
47606         "rw",
47607         "250"
47608       ],
47609       [
47610         "Saint Barthélemy",
47611         "bl",
47612         "590",
47613         1
47614       ],
47615       [
47616         "Saint Helena",
47617         "sh",
47618         "290"
47619       ],
47620       [
47621         "Saint Kitts and Nevis",
47622         "kn",
47623         "1869"
47624       ],
47625       [
47626         "Saint Lucia",
47627         "lc",
47628         "1758"
47629       ],
47630       [
47631         "Saint Martin (Saint-Martin (partie française))",
47632         "mf",
47633         "590",
47634         2
47635       ],
47636       [
47637         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47638         "pm",
47639         "508"
47640       ],
47641       [
47642         "Saint Vincent and the Grenadines",
47643         "vc",
47644         "1784"
47645       ],
47646       [
47647         "Samoa",
47648         "ws",
47649         "685"
47650       ],
47651       [
47652         "San Marino",
47653         "sm",
47654         "378"
47655       ],
47656       [
47657         "São Tomé and Príncipe (São Tomé e Príncipe)",
47658         "st",
47659         "239"
47660       ],
47661       [
47662         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47663         "sa",
47664         "966"
47665       ],
47666       [
47667         "Senegal (Sénégal)",
47668         "sn",
47669         "221"
47670       ],
47671       [
47672         "Serbia (Србија)",
47673         "rs",
47674         "381"
47675       ],
47676       [
47677         "Seychelles",
47678         "sc",
47679         "248"
47680       ],
47681       [
47682         "Sierra Leone",
47683         "sl",
47684         "232"
47685       ],
47686       [
47687         "Singapore",
47688         "sg",
47689         "65"
47690       ],
47691       [
47692         "Sint Maarten",
47693         "sx",
47694         "1721"
47695       ],
47696       [
47697         "Slovakia (Slovensko)",
47698         "sk",
47699         "421"
47700       ],
47701       [
47702         "Slovenia (Slovenija)",
47703         "si",
47704         "386"
47705       ],
47706       [
47707         "Solomon Islands",
47708         "sb",
47709         "677"
47710       ],
47711       [
47712         "Somalia (Soomaaliya)",
47713         "so",
47714         "252"
47715       ],
47716       [
47717         "South Africa",
47718         "za",
47719         "27"
47720       ],
47721       [
47722         "South Korea (대한민국)",
47723         "kr",
47724         "82"
47725       ],
47726       [
47727         "South Sudan (‫جنوب السودان‬‎)",
47728         "ss",
47729         "211"
47730       ],
47731       [
47732         "Spain (España)",
47733         "es",
47734         "34"
47735       ],
47736       [
47737         "Sri Lanka (ශ්‍රී ලංකාව)",
47738         "lk",
47739         "94"
47740       ],
47741       [
47742         "Sudan (‫السودان‬‎)",
47743         "sd",
47744         "249"
47745       ],
47746       [
47747         "Suriname",
47748         "sr",
47749         "597"
47750       ],
47751       [
47752         "Svalbard and Jan Mayen",
47753         "sj",
47754         "47",
47755         1
47756       ],
47757       [
47758         "Swaziland",
47759         "sz",
47760         "268"
47761       ],
47762       [
47763         "Sweden (Sverige)",
47764         "se",
47765         "46"
47766       ],
47767       [
47768         "Switzerland (Schweiz)",
47769         "ch",
47770         "41"
47771       ],
47772       [
47773         "Syria (‫سوريا‬‎)",
47774         "sy",
47775         "963"
47776       ],
47777       [
47778         "Taiwan (台灣)",
47779         "tw",
47780         "886"
47781       ],
47782       [
47783         "Tajikistan",
47784         "tj",
47785         "992"
47786       ],
47787       [
47788         "Tanzania",
47789         "tz",
47790         "255"
47791       ],
47792       [
47793         "Thailand (ไทย)",
47794         "th",
47795         "66"
47796       ],
47797       [
47798         "Timor-Leste",
47799         "tl",
47800         "670"
47801       ],
47802       [
47803         "Togo",
47804         "tg",
47805         "228"
47806       ],
47807       [
47808         "Tokelau",
47809         "tk",
47810         "690"
47811       ],
47812       [
47813         "Tonga",
47814         "to",
47815         "676"
47816       ],
47817       [
47818         "Trinidad and Tobago",
47819         "tt",
47820         "1868"
47821       ],
47822       [
47823         "Tunisia (‫تونس‬‎)",
47824         "tn",
47825         "216"
47826       ],
47827       [
47828         "Turkey (Türkiye)",
47829         "tr",
47830         "90"
47831       ],
47832       [
47833         "Turkmenistan",
47834         "tm",
47835         "993"
47836       ],
47837       [
47838         "Turks and Caicos Islands",
47839         "tc",
47840         "1649"
47841       ],
47842       [
47843         "Tuvalu",
47844         "tv",
47845         "688"
47846       ],
47847       [
47848         "U.S. Virgin Islands",
47849         "vi",
47850         "1340"
47851       ],
47852       [
47853         "Uganda",
47854         "ug",
47855         "256"
47856       ],
47857       [
47858         "Ukraine (Україна)",
47859         "ua",
47860         "380"
47861       ],
47862       [
47863         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47864         "ae",
47865         "971"
47866       ],
47867       [
47868         "United Kingdom",
47869         "gb",
47870         "44",
47871         0
47872       ],
47873       [
47874         "United States",
47875         "us",
47876         "1",
47877         0
47878       ],
47879       [
47880         "Uruguay",
47881         "uy",
47882         "598"
47883       ],
47884       [
47885         "Uzbekistan (Oʻzbekiston)",
47886         "uz",
47887         "998"
47888       ],
47889       [
47890         "Vanuatu",
47891         "vu",
47892         "678"
47893       ],
47894       [
47895         "Vatican City (Città del Vaticano)",
47896         "va",
47897         "39",
47898         1
47899       ],
47900       [
47901         "Venezuela",
47902         "ve",
47903         "58"
47904       ],
47905       [
47906         "Vietnam (Việt Nam)",
47907         "vn",
47908         "84"
47909       ],
47910       [
47911         "Wallis and Futuna (Wallis-et-Futuna)",
47912         "wf",
47913         "681"
47914       ],
47915       [
47916         "Western Sahara (‫الصحراء الغربية‬‎)",
47917         "eh",
47918         "212",
47919         1
47920       ],
47921       [
47922         "Yemen (‫اليمن‬‎)",
47923         "ye",
47924         "967"
47925       ],
47926       [
47927         "Zambia",
47928         "zm",
47929         "260"
47930       ],
47931       [
47932         "Zimbabwe",
47933         "zw",
47934         "263"
47935       ],
47936       [
47937         "Åland Islands",
47938         "ax",
47939         "358",
47940         1
47941       ]
47942   ];
47943   
47944   return d;
47945 }/**
47946 *    This script refer to:
47947 *    Title: International Telephone Input
47948 *    Author: Jack O'Connor
47949 *    Code version:  v12.1.12
47950 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47951 **/
47952
47953 /**
47954  * @class Roo.bootstrap.form.PhoneInput
47955  * @extends Roo.bootstrap.form.TriggerField
47956  * An input with International dial-code selection
47957  
47958  * @cfg {String} defaultDialCode default '+852'
47959  * @cfg {Array} preferedCountries default []
47960   
47961  * @constructor
47962  * Create a new PhoneInput.
47963  * @param {Object} config Configuration options
47964  */
47965
47966 Roo.bootstrap.form.PhoneInput = function(config) {
47967     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47968 };
47969
47970 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47971         /**
47972         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47973         */
47974         listWidth: undefined,
47975         
47976         selectedClass: 'active',
47977         
47978         invalidClass : "has-warning",
47979         
47980         validClass: 'has-success',
47981         
47982         allowed: '0123456789',
47983         
47984         max_length: 15,
47985         
47986         /**
47987          * @cfg {String} defaultDialCode The default dial code when initializing the input
47988          */
47989         defaultDialCode: '+852',
47990         
47991         /**
47992          * @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
47993          */
47994         preferedCountries: false,
47995         
47996         getAutoCreate : function()
47997         {
47998             var data = Roo.bootstrap.form.PhoneInputData();
47999             var align = this.labelAlign || this.parentLabelAlign();
48000             var id = Roo.id();
48001             
48002             this.allCountries = [];
48003             this.dialCodeMapping = [];
48004             
48005             for (var i = 0; i < data.length; i++) {
48006               var c = data[i];
48007               this.allCountries[i] = {
48008                 name: c[0],
48009                 iso2: c[1],
48010                 dialCode: c[2],
48011                 priority: c[3] || 0,
48012                 areaCodes: c[4] || null
48013               };
48014               this.dialCodeMapping[c[2]] = {
48015                   name: c[0],
48016                   iso2: c[1],
48017                   priority: c[3] || 0,
48018                   areaCodes: c[4] || null
48019               };
48020             }
48021             
48022             var cfg = {
48023                 cls: 'form-group',
48024                 cn: []
48025             };
48026             
48027             var input =  {
48028                 tag: 'input',
48029                 id : id,
48030                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48031                 maxlength: this.max_length,
48032                 cls : 'form-control tel-input',
48033                 autocomplete: 'new-password'
48034             };
48035             
48036             var hiddenInput = {
48037                 tag: 'input',
48038                 type: 'hidden',
48039                 cls: 'hidden-tel-input'
48040             };
48041             
48042             if (this.name) {
48043                 hiddenInput.name = this.name;
48044             }
48045             
48046             if (this.disabled) {
48047                 input.disabled = true;
48048             }
48049             
48050             var flag_container = {
48051                 tag: 'div',
48052                 cls: 'flag-box',
48053                 cn: [
48054                     {
48055                         tag: 'div',
48056                         cls: 'flag'
48057                     },
48058                     {
48059                         tag: 'div',
48060                         cls: 'caret'
48061                     }
48062                 ]
48063             };
48064             
48065             var box = {
48066                 tag: 'div',
48067                 cls: this.hasFeedback ? 'has-feedback' : '',
48068                 cn: [
48069                     hiddenInput,
48070                     input,
48071                     {
48072                         tag: 'input',
48073                         cls: 'dial-code-holder',
48074                         disabled: true
48075                     }
48076                 ]
48077             };
48078             
48079             var container = {
48080                 cls: 'roo-select2-container input-group',
48081                 cn: [
48082                     flag_container,
48083                     box
48084                 ]
48085             };
48086             
48087             if (this.fieldLabel.length) {
48088                 var indicator = {
48089                     tag: 'i',
48090                     tooltip: 'This field is required'
48091                 };
48092                 
48093                 var label = {
48094                     tag: 'label',
48095                     'for':  id,
48096                     cls: 'control-label',
48097                     cn: []
48098                 };
48099                 
48100                 var label_text = {
48101                     tag: 'span',
48102                     html: this.fieldLabel
48103                 };
48104                 
48105                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48106                 label.cn = [
48107                     indicator,
48108                     label_text
48109                 ];
48110                 
48111                 if(this.indicatorpos == 'right') {
48112                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48113                     label.cn = [
48114                         label_text,
48115                         indicator
48116                     ];
48117                 }
48118                 
48119                 if(align == 'left') {
48120                     container = {
48121                         tag: 'div',
48122                         cn: [
48123                             container
48124                         ]
48125                     };
48126                     
48127                     if(this.labelWidth > 12){
48128                         label.style = "width: " + this.labelWidth + 'px';
48129                     }
48130                     if(this.labelWidth < 13 && this.labelmd == 0){
48131                         this.labelmd = this.labelWidth;
48132                     }
48133                     if(this.labellg > 0){
48134                         label.cls += ' col-lg-' + this.labellg;
48135                         input.cls += ' col-lg-' + (12 - this.labellg);
48136                     }
48137                     if(this.labelmd > 0){
48138                         label.cls += ' col-md-' + this.labelmd;
48139                         container.cls += ' col-md-' + (12 - this.labelmd);
48140                     }
48141                     if(this.labelsm > 0){
48142                         label.cls += ' col-sm-' + this.labelsm;
48143                         container.cls += ' col-sm-' + (12 - this.labelsm);
48144                     }
48145                     if(this.labelxs > 0){
48146                         label.cls += ' col-xs-' + this.labelxs;
48147                         container.cls += ' col-xs-' + (12 - this.labelxs);
48148                     }
48149                 }
48150             }
48151             
48152             cfg.cn = [
48153                 label,
48154                 container
48155             ];
48156             
48157             var settings = this;
48158             
48159             ['xs','sm','md','lg'].map(function(size){
48160                 if (settings[size]) {
48161                     cfg.cls += ' col-' + size + '-' + settings[size];
48162                 }
48163             });
48164             
48165             this.store = new Roo.data.Store({
48166                 proxy : new Roo.data.MemoryProxy({}),
48167                 reader : new Roo.data.JsonReader({
48168                     fields : [
48169                         {
48170                             'name' : 'name',
48171                             'type' : 'string'
48172                         },
48173                         {
48174                             'name' : 'iso2',
48175                             'type' : 'string'
48176                         },
48177                         {
48178                             'name' : 'dialCode',
48179                             'type' : 'string'
48180                         },
48181                         {
48182                             'name' : 'priority',
48183                             'type' : 'string'
48184                         },
48185                         {
48186                             'name' : 'areaCodes',
48187                             'type' : 'string'
48188                         }
48189                     ]
48190                 })
48191             });
48192             
48193             if(!this.preferedCountries) {
48194                 this.preferedCountries = [
48195                     'hk',
48196                     'gb',
48197                     'us'
48198                 ];
48199             }
48200             
48201             var p = this.preferedCountries.reverse();
48202             
48203             if(p) {
48204                 for (var i = 0; i < p.length; i++) {
48205                     for (var j = 0; j < this.allCountries.length; j++) {
48206                         if(this.allCountries[j].iso2 == p[i]) {
48207                             var t = this.allCountries[j];
48208                             this.allCountries.splice(j,1);
48209                             this.allCountries.unshift(t);
48210                         }
48211                     } 
48212                 }
48213             }
48214             
48215             this.store.proxy.data = {
48216                 success: true,
48217                 data: this.allCountries
48218             };
48219             
48220             return cfg;
48221         },
48222         
48223         initEvents : function()
48224         {
48225             this.createList();
48226             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48227             
48228             this.indicator = this.indicatorEl();
48229             this.flag = this.flagEl();
48230             this.dialCodeHolder = this.dialCodeHolderEl();
48231             
48232             this.trigger = this.el.select('div.flag-box',true).first();
48233             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48234             
48235             var _this = this;
48236             
48237             (function(){
48238                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48239                 _this.list.setWidth(lw);
48240             }).defer(100);
48241             
48242             this.list.on('mouseover', this.onViewOver, this);
48243             this.list.on('mousemove', this.onViewMove, this);
48244             this.inputEl().on("keyup", this.onKeyUp, this);
48245             this.inputEl().on("keypress", this.onKeyPress, this);
48246             
48247             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48248
48249             this.view = new Roo.View(this.list, this.tpl, {
48250                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48251             });
48252             
48253             this.view.on('click', this.onViewClick, this);
48254             this.setValue(this.defaultDialCode);
48255         },
48256         
48257         onTriggerClick : function(e)
48258         {
48259             Roo.log('trigger click');
48260             if(this.disabled){
48261                 return;
48262             }
48263             
48264             if(this.isExpanded()){
48265                 this.collapse();
48266                 this.hasFocus = false;
48267             }else {
48268                 this.store.load({});
48269                 this.hasFocus = true;
48270                 this.expand();
48271             }
48272         },
48273         
48274         isExpanded : function()
48275         {
48276             return this.list.isVisible();
48277         },
48278         
48279         collapse : function()
48280         {
48281             if(!this.isExpanded()){
48282                 return;
48283             }
48284             this.list.hide();
48285             Roo.get(document).un('mousedown', this.collapseIf, this);
48286             Roo.get(document).un('mousewheel', this.collapseIf, this);
48287             this.fireEvent('collapse', this);
48288             this.validate();
48289         },
48290         
48291         expand : function()
48292         {
48293             Roo.log('expand');
48294
48295             if(this.isExpanded() || !this.hasFocus){
48296                 return;
48297             }
48298             
48299             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48300             this.list.setWidth(lw);
48301             
48302             this.list.show();
48303             this.restrictHeight();
48304             
48305             Roo.get(document).on('mousedown', this.collapseIf, this);
48306             Roo.get(document).on('mousewheel', this.collapseIf, this);
48307             
48308             this.fireEvent('expand', this);
48309         },
48310         
48311         restrictHeight : function()
48312         {
48313             this.list.alignTo(this.inputEl(), this.listAlign);
48314             this.list.alignTo(this.inputEl(), this.listAlign);
48315         },
48316         
48317         onViewOver : function(e, t)
48318         {
48319             if(this.inKeyMode){
48320                 return;
48321             }
48322             var item = this.view.findItemFromChild(t);
48323             
48324             if(item){
48325                 var index = this.view.indexOf(item);
48326                 this.select(index, false);
48327             }
48328         },
48329
48330         // private
48331         onViewClick : function(view, doFocus, el, e)
48332         {
48333             var index = this.view.getSelectedIndexes()[0];
48334             
48335             var r = this.store.getAt(index);
48336             
48337             if(r){
48338                 this.onSelect(r, index);
48339             }
48340             if(doFocus !== false && !this.blockFocus){
48341                 this.inputEl().focus();
48342             }
48343         },
48344         
48345         onViewMove : function(e, t)
48346         {
48347             this.inKeyMode = false;
48348         },
48349         
48350         select : function(index, scrollIntoView)
48351         {
48352             this.selectedIndex = index;
48353             this.view.select(index);
48354             if(scrollIntoView !== false){
48355                 var el = this.view.getNode(index);
48356                 if(el){
48357                     this.list.scrollChildIntoView(el, false);
48358                 }
48359             }
48360         },
48361         
48362         createList : function()
48363         {
48364             this.list = Roo.get(document.body).createChild({
48365                 tag: 'ul',
48366                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48367                 style: 'display:none'
48368             });
48369             
48370             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48371         },
48372         
48373         collapseIf : function(e)
48374         {
48375             var in_combo  = e.within(this.el);
48376             var in_list =  e.within(this.list);
48377             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48378             
48379             if (in_combo || in_list || is_list) {
48380                 return;
48381             }
48382             this.collapse();
48383         },
48384         
48385         onSelect : function(record, index)
48386         {
48387             if(this.fireEvent('beforeselect', this, record, index) !== false){
48388                 
48389                 this.setFlagClass(record.data.iso2);
48390                 this.setDialCode(record.data.dialCode);
48391                 this.hasFocus = false;
48392                 this.collapse();
48393                 this.fireEvent('select', this, record, index);
48394             }
48395         },
48396         
48397         flagEl : function()
48398         {
48399             var flag = this.el.select('div.flag',true).first();
48400             if(!flag){
48401                 return false;
48402             }
48403             return flag;
48404         },
48405         
48406         dialCodeHolderEl : function()
48407         {
48408             var d = this.el.select('input.dial-code-holder',true).first();
48409             if(!d){
48410                 return false;
48411             }
48412             return d;
48413         },
48414         
48415         setDialCode : function(v)
48416         {
48417             this.dialCodeHolder.dom.value = '+'+v;
48418         },
48419         
48420         setFlagClass : function(n)
48421         {
48422             this.flag.dom.className = 'flag '+n;
48423         },
48424         
48425         getValue : function()
48426         {
48427             var v = this.inputEl().getValue();
48428             if(this.dialCodeHolder) {
48429                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48430             }
48431             return v;
48432         },
48433         
48434         setValue : function(v)
48435         {
48436             var d = this.getDialCode(v);
48437             
48438             //invalid dial code
48439             if(v.length == 0 || !d || d.length == 0) {
48440                 if(this.rendered){
48441                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48442                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48443                 }
48444                 return;
48445             }
48446             
48447             //valid dial code
48448             this.setFlagClass(this.dialCodeMapping[d].iso2);
48449             this.setDialCode(d);
48450             this.inputEl().dom.value = v.replace('+'+d,'');
48451             this.hiddenEl().dom.value = this.getValue();
48452             
48453             this.validate();
48454         },
48455         
48456         getDialCode : function(v)
48457         {
48458             v = v ||  '';
48459             
48460             if (v.length == 0) {
48461                 return this.dialCodeHolder.dom.value;
48462             }
48463             
48464             var dialCode = "";
48465             if (v.charAt(0) != "+") {
48466                 return false;
48467             }
48468             var numericChars = "";
48469             for (var i = 1; i < v.length; i++) {
48470               var c = v.charAt(i);
48471               if (!isNaN(c)) {
48472                 numericChars += c;
48473                 if (this.dialCodeMapping[numericChars]) {
48474                   dialCode = v.substr(1, i);
48475                 }
48476                 if (numericChars.length == 4) {
48477                   break;
48478                 }
48479               }
48480             }
48481             return dialCode;
48482         },
48483         
48484         reset : function()
48485         {
48486             this.setValue(this.defaultDialCode);
48487             this.validate();
48488         },
48489         
48490         hiddenEl : function()
48491         {
48492             return this.el.select('input.hidden-tel-input',true).first();
48493         },
48494         
48495         // after setting val
48496         onKeyUp : function(e){
48497             this.setValue(this.getValue());
48498         },
48499         
48500         onKeyPress : function(e){
48501             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48502                 e.stopEvent();
48503             }
48504         }
48505         
48506 });
48507 /**
48508  * @class Roo.bootstrap.form.MoneyField
48509  * @extends Roo.bootstrap.form.ComboBox
48510  * Bootstrap MoneyField class
48511  * 
48512  * @constructor
48513  * Create a new MoneyField.
48514  * @param {Object} config Configuration options
48515  */
48516
48517 Roo.bootstrap.form.MoneyField = function(config) {
48518     
48519     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48520     
48521 };
48522
48523 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48524     
48525     /**
48526      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48527      */
48528     allowDecimals : true,
48529     /**
48530      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48531      */
48532     decimalSeparator : ".",
48533     /**
48534      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48535      */
48536     decimalPrecision : 0,
48537     /**
48538      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48539      */
48540     allowNegative : true,
48541     /**
48542      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48543      */
48544     allowZero: true,
48545     /**
48546      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48547      */
48548     minValue : Number.NEGATIVE_INFINITY,
48549     /**
48550      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48551      */
48552     maxValue : Number.MAX_VALUE,
48553     /**
48554      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48555      */
48556     minText : "The minimum value for this field is {0}",
48557     /**
48558      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48559      */
48560     maxText : "The maximum value for this field is {0}",
48561     /**
48562      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48563      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48564      */
48565     nanText : "{0} is not a valid number",
48566     /**
48567      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48568      */
48569     castInt : true,
48570     /**
48571      * @cfg {String} defaults currency of the MoneyField
48572      * value should be in lkey
48573      */
48574     defaultCurrency : false,
48575     /**
48576      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48577      */
48578     thousandsDelimiter : false,
48579     /**
48580      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48581      */
48582     max_length: false,
48583     
48584     inputlg : 9,
48585     inputmd : 9,
48586     inputsm : 9,
48587     inputxs : 6,
48588      /**
48589      * @cfg {Roo.data.Store} store  Store to lookup currency??
48590      */
48591     store : false,
48592     
48593     getAutoCreate : function()
48594     {
48595         var align = this.labelAlign || this.parentLabelAlign();
48596         
48597         var id = Roo.id();
48598
48599         var cfg = {
48600             cls: 'form-group',
48601             cn: []
48602         };
48603
48604         var input =  {
48605             tag: 'input',
48606             id : id,
48607             cls : 'form-control roo-money-amount-input',
48608             autocomplete: 'new-password'
48609         };
48610         
48611         var hiddenInput = {
48612             tag: 'input',
48613             type: 'hidden',
48614             id: Roo.id(),
48615             cls: 'hidden-number-input'
48616         };
48617         
48618         if(this.max_length) {
48619             input.maxlength = this.max_length; 
48620         }
48621         
48622         if (this.name) {
48623             hiddenInput.name = this.name;
48624         }
48625
48626         if (this.disabled) {
48627             input.disabled = true;
48628         }
48629
48630         var clg = 12 - this.inputlg;
48631         var cmd = 12 - this.inputmd;
48632         var csm = 12 - this.inputsm;
48633         var cxs = 12 - this.inputxs;
48634         
48635         var container = {
48636             tag : 'div',
48637             cls : 'row roo-money-field',
48638             cn : [
48639                 {
48640                     tag : 'div',
48641                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48642                     cn : [
48643                         {
48644                             tag : 'div',
48645                             cls: 'roo-select2-container input-group',
48646                             cn: [
48647                                 {
48648                                     tag : 'input',
48649                                     cls : 'form-control roo-money-currency-input',
48650                                     autocomplete: 'new-password',
48651                                     readOnly : 1,
48652                                     name : this.currencyName
48653                                 },
48654                                 {
48655                                     tag :'span',
48656                                     cls : 'input-group-addon',
48657                                     cn : [
48658                                         {
48659                                             tag: 'span',
48660                                             cls: 'caret'
48661                                         }
48662                                     ]
48663                                 }
48664                             ]
48665                         }
48666                     ]
48667                 },
48668                 {
48669                     tag : 'div',
48670                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48671                     cn : [
48672                         {
48673                             tag: 'div',
48674                             cls: this.hasFeedback ? 'has-feedback' : '',
48675                             cn: [
48676                                 input
48677                             ]
48678                         }
48679                     ]
48680                 }
48681             ]
48682             
48683         };
48684         
48685         if (this.fieldLabel.length) {
48686             var indicator = {
48687                 tag: 'i',
48688                 tooltip: 'This field is required'
48689             };
48690
48691             var label = {
48692                 tag: 'label',
48693                 'for':  id,
48694                 cls: 'control-label',
48695                 cn: []
48696             };
48697
48698             var label_text = {
48699                 tag: 'span',
48700                 html: this.fieldLabel
48701             };
48702
48703             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48704             label.cn = [
48705                 indicator,
48706                 label_text
48707             ];
48708
48709             if(this.indicatorpos == 'right') {
48710                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48711                 label.cn = [
48712                     label_text,
48713                     indicator
48714                 ];
48715             }
48716
48717             if(align == 'left') {
48718                 container = {
48719                     tag: 'div',
48720                     cn: [
48721                         container
48722                     ]
48723                 };
48724
48725                 if(this.labelWidth > 12){
48726                     label.style = "width: " + this.labelWidth + 'px';
48727                 }
48728                 if(this.labelWidth < 13 && this.labelmd == 0){
48729                     this.labelmd = this.labelWidth;
48730                 }
48731                 if(this.labellg > 0){
48732                     label.cls += ' col-lg-' + this.labellg;
48733                     input.cls += ' col-lg-' + (12 - this.labellg);
48734                 }
48735                 if(this.labelmd > 0){
48736                     label.cls += ' col-md-' + this.labelmd;
48737                     container.cls += ' col-md-' + (12 - this.labelmd);
48738                 }
48739                 if(this.labelsm > 0){
48740                     label.cls += ' col-sm-' + this.labelsm;
48741                     container.cls += ' col-sm-' + (12 - this.labelsm);
48742                 }
48743                 if(this.labelxs > 0){
48744                     label.cls += ' col-xs-' + this.labelxs;
48745                     container.cls += ' col-xs-' + (12 - this.labelxs);
48746                 }
48747             }
48748         }
48749
48750         cfg.cn = [
48751             label,
48752             container,
48753             hiddenInput
48754         ];
48755         
48756         var settings = this;
48757
48758         ['xs','sm','md','lg'].map(function(size){
48759             if (settings[size]) {
48760                 cfg.cls += ' col-' + size + '-' + settings[size];
48761             }
48762         });
48763         
48764         return cfg;
48765     },
48766     
48767     initEvents : function()
48768     {
48769         this.indicator = this.indicatorEl();
48770         
48771         this.initCurrencyEvent();
48772         
48773         this.initNumberEvent();
48774     },
48775     
48776     initCurrencyEvent : function()
48777     {
48778         if (!this.store) {
48779             throw "can not find store for combo";
48780         }
48781         
48782         this.store = Roo.factory(this.store, Roo.data);
48783         this.store.parent = this;
48784         
48785         this.createList();
48786         
48787         this.triggerEl = this.el.select('.input-group-addon', true).first();
48788         
48789         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48790         
48791         var _this = this;
48792         
48793         (function(){
48794             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48795             _this.list.setWidth(lw);
48796         }).defer(100);
48797         
48798         this.list.on('mouseover', this.onViewOver, this);
48799         this.list.on('mousemove', this.onViewMove, this);
48800         this.list.on('scroll', this.onViewScroll, this);
48801         
48802         if(!this.tpl){
48803             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48804         }
48805         
48806         this.view = new Roo.View(this.list, this.tpl, {
48807             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48808         });
48809         
48810         this.view.on('click', this.onViewClick, this);
48811         
48812         this.store.on('beforeload', this.onBeforeLoad, this);
48813         this.store.on('load', this.onLoad, this);
48814         this.store.on('loadexception', this.onLoadException, this);
48815         
48816         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48817             "up" : function(e){
48818                 this.inKeyMode = true;
48819                 this.selectPrev();
48820             },
48821
48822             "down" : function(e){
48823                 if(!this.isExpanded()){
48824                     this.onTriggerClick();
48825                 }else{
48826                     this.inKeyMode = true;
48827                     this.selectNext();
48828                 }
48829             },
48830
48831             "enter" : function(e){
48832                 this.collapse();
48833                 
48834                 if(this.fireEvent("specialkey", this, e)){
48835                     this.onViewClick(false);
48836                 }
48837                 
48838                 return true;
48839             },
48840
48841             "esc" : function(e){
48842                 this.collapse();
48843             },
48844
48845             "tab" : function(e){
48846                 this.collapse();
48847                 
48848                 if(this.fireEvent("specialkey", this, e)){
48849                     this.onViewClick(false);
48850                 }
48851                 
48852                 return true;
48853             },
48854
48855             scope : this,
48856
48857             doRelay : function(foo, bar, hname){
48858                 if(hname == 'down' || this.scope.isExpanded()){
48859                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48860                 }
48861                 return true;
48862             },
48863
48864             forceKeyDown: true
48865         });
48866         
48867         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48868         
48869     },
48870     
48871     initNumberEvent : function(e)
48872     {
48873         this.inputEl().on("keydown" , this.fireKey,  this);
48874         this.inputEl().on("focus", this.onFocus,  this);
48875         this.inputEl().on("blur", this.onBlur,  this);
48876         
48877         this.inputEl().relayEvent('keyup', this);
48878         
48879         if(this.indicator){
48880             this.indicator.addClass('invisible');
48881         }
48882  
48883         this.originalValue = this.getValue();
48884         
48885         if(this.validationEvent == 'keyup'){
48886             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48887             this.inputEl().on('keyup', this.filterValidation, this);
48888         }
48889         else if(this.validationEvent !== false){
48890             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48891         }
48892         
48893         if(this.selectOnFocus){
48894             this.on("focus", this.preFocus, this);
48895             
48896         }
48897         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48898             this.inputEl().on("keypress", this.filterKeys, this);
48899         } else {
48900             this.inputEl().relayEvent('keypress', this);
48901         }
48902         
48903         var allowed = "0123456789";
48904         
48905         if(this.allowDecimals){
48906             allowed += this.decimalSeparator;
48907         }
48908         
48909         if(this.allowNegative){
48910             allowed += "-";
48911         }
48912         
48913         if(this.thousandsDelimiter) {
48914             allowed += ",";
48915         }
48916         
48917         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48918         
48919         var keyPress = function(e){
48920             
48921             var k = e.getKey();
48922             
48923             var c = e.getCharCode();
48924             
48925             if(
48926                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48927                     allowed.indexOf(String.fromCharCode(c)) === -1
48928             ){
48929                 e.stopEvent();
48930                 return;
48931             }
48932             
48933             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48934                 return;
48935             }
48936             
48937             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48938                 e.stopEvent();
48939             }
48940         };
48941         
48942         this.inputEl().on("keypress", keyPress, this);
48943         
48944     },
48945     
48946     onTriggerClick : function(e)
48947     {   
48948         if(this.disabled){
48949             return;
48950         }
48951         
48952         this.page = 0;
48953         this.loadNext = false;
48954         
48955         if(this.isExpanded()){
48956             this.collapse();
48957             return;
48958         }
48959         
48960         this.hasFocus = true;
48961         
48962         if(this.triggerAction == 'all') {
48963             this.doQuery(this.allQuery, true);
48964             return;
48965         }
48966         
48967         this.doQuery(this.getRawValue());
48968     },
48969     
48970     getCurrency : function()
48971     {   
48972         var v = this.currencyEl().getValue();
48973         
48974         return v;
48975     },
48976     
48977     restrictHeight : function()
48978     {
48979         this.list.alignTo(this.currencyEl(), this.listAlign);
48980         this.list.alignTo(this.currencyEl(), this.listAlign);
48981     },
48982     
48983     onViewClick : function(view, doFocus, el, e)
48984     {
48985         var index = this.view.getSelectedIndexes()[0];
48986         
48987         var r = this.store.getAt(index);
48988         
48989         if(r){
48990             this.onSelect(r, index);
48991         }
48992     },
48993     
48994     onSelect : function(record, index){
48995         
48996         if(this.fireEvent('beforeselect', this, record, index) !== false){
48997         
48998             this.setFromCurrencyData(index > -1 ? record.data : false);
48999             
49000             this.collapse();
49001             
49002             this.fireEvent('select', this, record, index);
49003         }
49004     },
49005     
49006     setFromCurrencyData : function(o)
49007     {
49008         var currency = '';
49009         
49010         this.lastCurrency = o;
49011         
49012         if (this.currencyField) {
49013             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49014         } else {
49015             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49016         }
49017         
49018         this.lastSelectionText = currency;
49019         
49020         //setting default currency
49021         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49022             this.setCurrency(this.defaultCurrency);
49023             return;
49024         }
49025         
49026         this.setCurrency(currency);
49027     },
49028     
49029     setFromData : function(o)
49030     {
49031         var c = {};
49032         
49033         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49034         
49035         this.setFromCurrencyData(c);
49036         
49037         var value = '';
49038         
49039         if (this.name) {
49040             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49041         } else {
49042             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49043         }
49044         
49045         this.setValue(value);
49046         
49047     },
49048     
49049     setCurrency : function(v)
49050     {   
49051         this.currencyValue = v;
49052         
49053         if(this.rendered){
49054             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49055             this.validate();
49056         }
49057     },
49058     
49059     setValue : function(v)
49060     {
49061         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49062         
49063         this.value = v;
49064         
49065         if(this.rendered){
49066             
49067             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49068             
49069             this.inputEl().dom.value = (v == '') ? '' :
49070                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49071             
49072             if(!this.allowZero && v === '0') {
49073                 this.hiddenEl().dom.value = '';
49074                 this.inputEl().dom.value = '';
49075             }
49076             
49077             this.validate();
49078         }
49079     },
49080     
49081     getRawValue : function()
49082     {
49083         var v = this.inputEl().getValue();
49084         
49085         return v;
49086     },
49087     
49088     getValue : function()
49089     {
49090         return this.fixPrecision(this.parseValue(this.getRawValue()));
49091     },
49092     
49093     parseValue : function(value)
49094     {
49095         if(this.thousandsDelimiter) {
49096             value += "";
49097             r = new RegExp(",", "g");
49098             value = value.replace(r, "");
49099         }
49100         
49101         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49102         return isNaN(value) ? '' : value;
49103         
49104     },
49105     
49106     fixPrecision : function(value)
49107     {
49108         if(this.thousandsDelimiter) {
49109             value += "";
49110             r = new RegExp(",", "g");
49111             value = value.replace(r, "");
49112         }
49113         
49114         var nan = isNaN(value);
49115         
49116         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49117             return nan ? '' : value;
49118         }
49119         return parseFloat(value).toFixed(this.decimalPrecision);
49120     },
49121     
49122     decimalPrecisionFcn : function(v)
49123     {
49124         return Math.floor(v);
49125     },
49126     
49127     validateValue : function(value)
49128     {
49129         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49130             return false;
49131         }
49132         
49133         var num = this.parseValue(value);
49134         
49135         if(isNaN(num)){
49136             this.markInvalid(String.format(this.nanText, value));
49137             return false;
49138         }
49139         
49140         if(num < this.minValue){
49141             this.markInvalid(String.format(this.minText, this.minValue));
49142             return false;
49143         }
49144         
49145         if(num > this.maxValue){
49146             this.markInvalid(String.format(this.maxText, this.maxValue));
49147             return false;
49148         }
49149         
49150         return true;
49151     },
49152     
49153     validate : function()
49154     {
49155         if(this.disabled || this.allowBlank){
49156             this.markValid();
49157             return true;
49158         }
49159         
49160         var currency = this.getCurrency();
49161         
49162         if(this.validateValue(this.getRawValue()) && currency.length){
49163             this.markValid();
49164             return true;
49165         }
49166         
49167         this.markInvalid();
49168         return false;
49169     },
49170     
49171     getName: function()
49172     {
49173         return this.name;
49174     },
49175     
49176     beforeBlur : function()
49177     {
49178         if(!this.castInt){
49179             return;
49180         }
49181         
49182         var v = this.parseValue(this.getRawValue());
49183         
49184         if(v || v == 0){
49185             this.setValue(v);
49186         }
49187     },
49188     
49189     onBlur : function()
49190     {
49191         this.beforeBlur();
49192         
49193         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49194             //this.el.removeClass(this.focusClass);
49195         }
49196         
49197         this.hasFocus = false;
49198         
49199         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49200             this.validate();
49201         }
49202         
49203         var v = this.getValue();
49204         
49205         if(String(v) !== String(this.startValue)){
49206             this.fireEvent('change', this, v, this.startValue);
49207         }
49208         
49209         this.fireEvent("blur", this);
49210     },
49211     
49212     inputEl : function()
49213     {
49214         return this.el.select('.roo-money-amount-input', true).first();
49215     },
49216     
49217     currencyEl : function()
49218     {
49219         return this.el.select('.roo-money-currency-input', true).first();
49220     },
49221     
49222     hiddenEl : function()
49223     {
49224         return this.el.select('input.hidden-number-input',true).first();
49225     }
49226     
49227 });/**
49228  * @class Roo.bootstrap.BezierSignature
49229  * @extends Roo.bootstrap.Component
49230  * Bootstrap BezierSignature class
49231  * This script refer to:
49232  *    Title: Signature Pad
49233  *    Author: szimek
49234  *    Availability: https://github.com/szimek/signature_pad
49235  *
49236  * @constructor
49237  * Create a new BezierSignature
49238  * @param {Object} config The config object
49239  */
49240
49241 Roo.bootstrap.BezierSignature = function(config){
49242     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49243     this.addEvents({
49244         "resize" : true
49245     });
49246 };
49247
49248 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49249 {
49250      
49251     curve_data: [],
49252     
49253     is_empty: true,
49254     
49255     mouse_btn_down: true,
49256     
49257     /**
49258      * @cfg {int} canvas height
49259      */
49260     canvas_height: '200px',
49261     
49262     /**
49263      * @cfg {float|function} Radius of a single dot.
49264      */ 
49265     dot_size: false,
49266     
49267     /**
49268      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49269      */
49270     min_width: 0.5,
49271     
49272     /**
49273      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49274      */
49275     max_width: 2.5,
49276     
49277     /**
49278      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49279      */
49280     throttle: 16,
49281     
49282     /**
49283      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49284      */
49285     min_distance: 5,
49286     
49287     /**
49288      * @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.
49289      */
49290     bg_color: 'rgba(0, 0, 0, 0)',
49291     
49292     /**
49293      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49294      */
49295     dot_color: 'black',
49296     
49297     /**
49298      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49299      */ 
49300     velocity_filter_weight: 0.7,
49301     
49302     /**
49303      * @cfg {function} Callback when stroke begin. 
49304      */
49305     onBegin: false,
49306     
49307     /**
49308      * @cfg {function} Callback when stroke end.
49309      */
49310     onEnd: false,
49311     
49312     getAutoCreate : function()
49313     {
49314         var cls = 'roo-signature column';
49315         
49316         if(this.cls){
49317             cls += ' ' + this.cls;
49318         }
49319         
49320         var col_sizes = [
49321             'lg',
49322             'md',
49323             'sm',
49324             'xs'
49325         ];
49326         
49327         for(var i = 0; i < col_sizes.length; i++) {
49328             if(this[col_sizes[i]]) {
49329                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49330             }
49331         }
49332         
49333         var cfg = {
49334             tag: 'div',
49335             cls: cls,
49336             cn: [
49337                 {
49338                     tag: 'div',
49339                     cls: 'roo-signature-body',
49340                     cn: [
49341                         {
49342                             tag: 'canvas',
49343                             cls: 'roo-signature-body-canvas',
49344                             height: this.canvas_height,
49345                             width: this.canvas_width
49346                         }
49347                     ]
49348                 },
49349                 {
49350                     tag: 'input',
49351                     type: 'file',
49352                     style: 'display: none'
49353                 }
49354             ]
49355         };
49356         
49357         return cfg;
49358     },
49359     
49360     initEvents: function() 
49361     {
49362         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49363         
49364         var canvas = this.canvasEl();
49365         
49366         // mouse && touch event swapping...
49367         canvas.dom.style.touchAction = 'none';
49368         canvas.dom.style.msTouchAction = 'none';
49369         
49370         this.mouse_btn_down = false;
49371         canvas.on('mousedown', this._handleMouseDown, this);
49372         canvas.on('mousemove', this._handleMouseMove, this);
49373         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49374         
49375         if (window.PointerEvent) {
49376             canvas.on('pointerdown', this._handleMouseDown, this);
49377             canvas.on('pointermove', this._handleMouseMove, this);
49378             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49379         }
49380         
49381         if ('ontouchstart' in window) {
49382             canvas.on('touchstart', this._handleTouchStart, this);
49383             canvas.on('touchmove', this._handleTouchMove, this);
49384             canvas.on('touchend', this._handleTouchEnd, this);
49385         }
49386         
49387         Roo.EventManager.onWindowResize(this.resize, this, true);
49388         
49389         // file input event
49390         this.fileEl().on('change', this.uploadImage, this);
49391         
49392         this.clear();
49393         
49394         this.resize();
49395     },
49396     
49397     resize: function(){
49398         
49399         var canvas = this.canvasEl().dom;
49400         var ctx = this.canvasElCtx();
49401         var img_data = false;
49402         
49403         if(canvas.width > 0) {
49404             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49405         }
49406         // setting canvas width will clean img data
49407         canvas.width = 0;
49408         
49409         var style = window.getComputedStyle ? 
49410             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49411             
49412         var padding_left = parseInt(style.paddingLeft) || 0;
49413         var padding_right = parseInt(style.paddingRight) || 0;
49414         
49415         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49416         
49417         if(img_data) {
49418             ctx.putImageData(img_data, 0, 0);
49419         }
49420     },
49421     
49422     _handleMouseDown: function(e)
49423     {
49424         if (e.browserEvent.which === 1) {
49425             this.mouse_btn_down = true;
49426             this.strokeBegin(e);
49427         }
49428     },
49429     
49430     _handleMouseMove: function (e)
49431     {
49432         if (this.mouse_btn_down) {
49433             this.strokeMoveUpdate(e);
49434         }
49435     },
49436     
49437     _handleMouseUp: function (e)
49438     {
49439         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49440             this.mouse_btn_down = false;
49441             this.strokeEnd(e);
49442         }
49443     },
49444     
49445     _handleTouchStart: function (e) {
49446         
49447         e.preventDefault();
49448         if (e.browserEvent.targetTouches.length === 1) {
49449             // var touch = e.browserEvent.changedTouches[0];
49450             // this.strokeBegin(touch);
49451             
49452              this.strokeBegin(e); // assume e catching the correct xy...
49453         }
49454     },
49455     
49456     _handleTouchMove: function (e) {
49457         e.preventDefault();
49458         // var touch = event.targetTouches[0];
49459         // _this._strokeMoveUpdate(touch);
49460         this.strokeMoveUpdate(e);
49461     },
49462     
49463     _handleTouchEnd: function (e) {
49464         var wasCanvasTouched = e.target === this.canvasEl().dom;
49465         if (wasCanvasTouched) {
49466             e.preventDefault();
49467             // var touch = event.changedTouches[0];
49468             // _this._strokeEnd(touch);
49469             this.strokeEnd(e);
49470         }
49471     },
49472     
49473     reset: function () {
49474         this._lastPoints = [];
49475         this._lastVelocity = 0;
49476         this._lastWidth = (this.min_width + this.max_width) / 2;
49477         this.canvasElCtx().fillStyle = this.dot_color;
49478     },
49479     
49480     strokeMoveUpdate: function(e)
49481     {
49482         this.strokeUpdate(e);
49483         
49484         if (this.throttle) {
49485             this.throttleStroke(this.strokeUpdate, this.throttle);
49486         }
49487         else {
49488             this.strokeUpdate(e);
49489         }
49490     },
49491     
49492     strokeBegin: function(e)
49493     {
49494         var newPointGroup = {
49495             color: this.dot_color,
49496             points: []
49497         };
49498         
49499         if (typeof this.onBegin === 'function') {
49500             this.onBegin(e);
49501         }
49502         
49503         this.curve_data.push(newPointGroup);
49504         this.reset();
49505         this.strokeUpdate(e);
49506     },
49507     
49508     strokeUpdate: function(e)
49509     {
49510         var rect = this.canvasEl().dom.getBoundingClientRect();
49511         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49512         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49513         var lastPoints = lastPointGroup.points;
49514         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49515         var isLastPointTooClose = lastPoint
49516             ? point.distanceTo(lastPoint) <= this.min_distance
49517             : false;
49518         var color = lastPointGroup.color;
49519         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49520             var curve = this.addPoint(point);
49521             if (!lastPoint) {
49522                 this.drawDot({color: color, point: point});
49523             }
49524             else if (curve) {
49525                 this.drawCurve({color: color, curve: curve});
49526             }
49527             lastPoints.push({
49528                 time: point.time,
49529                 x: point.x,
49530                 y: point.y
49531             });
49532         }
49533     },
49534     
49535     strokeEnd: function(e)
49536     {
49537         this.strokeUpdate(e);
49538         if (typeof this.onEnd === 'function') {
49539             this.onEnd(e);
49540         }
49541     },
49542     
49543     addPoint:  function (point) {
49544         var _lastPoints = this._lastPoints;
49545         _lastPoints.push(point);
49546         if (_lastPoints.length > 2) {
49547             if (_lastPoints.length === 3) {
49548                 _lastPoints.unshift(_lastPoints[0]);
49549             }
49550             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49551             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49552             _lastPoints.shift();
49553             return curve;
49554         }
49555         return null;
49556     },
49557     
49558     calculateCurveWidths: function (startPoint, endPoint) {
49559         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49560             (1 - this.velocity_filter_weight) * this._lastVelocity;
49561
49562         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49563         var widths = {
49564             end: newWidth,
49565             start: this._lastWidth
49566         };
49567         
49568         this._lastVelocity = velocity;
49569         this._lastWidth = newWidth;
49570         return widths;
49571     },
49572     
49573     drawDot: function (_a) {
49574         var color = _a.color, point = _a.point;
49575         var ctx = this.canvasElCtx();
49576         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49577         ctx.beginPath();
49578         this.drawCurveSegment(point.x, point.y, width);
49579         ctx.closePath();
49580         ctx.fillStyle = color;
49581         ctx.fill();
49582     },
49583     
49584     drawCurve: function (_a) {
49585         var color = _a.color, curve = _a.curve;
49586         var ctx = this.canvasElCtx();
49587         var widthDelta = curve.endWidth - curve.startWidth;
49588         var drawSteps = Math.floor(curve.length()) * 2;
49589         ctx.beginPath();
49590         ctx.fillStyle = color;
49591         for (var i = 0; i < drawSteps; i += 1) {
49592         var t = i / drawSteps;
49593         var tt = t * t;
49594         var ttt = tt * t;
49595         var u = 1 - t;
49596         var uu = u * u;
49597         var uuu = uu * u;
49598         var x = uuu * curve.startPoint.x;
49599         x += 3 * uu * t * curve.control1.x;
49600         x += 3 * u * tt * curve.control2.x;
49601         x += ttt * curve.endPoint.x;
49602         var y = uuu * curve.startPoint.y;
49603         y += 3 * uu * t * curve.control1.y;
49604         y += 3 * u * tt * curve.control2.y;
49605         y += ttt * curve.endPoint.y;
49606         var width = curve.startWidth + ttt * widthDelta;
49607         this.drawCurveSegment(x, y, width);
49608         }
49609         ctx.closePath();
49610         ctx.fill();
49611     },
49612     
49613     drawCurveSegment: function (x, y, width) {
49614         var ctx = this.canvasElCtx();
49615         ctx.moveTo(x, y);
49616         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49617         this.is_empty = false;
49618     },
49619     
49620     clear: function()
49621     {
49622         var ctx = this.canvasElCtx();
49623         var canvas = this.canvasEl().dom;
49624         ctx.fillStyle = this.bg_color;
49625         ctx.clearRect(0, 0, canvas.width, canvas.height);
49626         ctx.fillRect(0, 0, canvas.width, canvas.height);
49627         this.curve_data = [];
49628         this.reset();
49629         this.is_empty = true;
49630     },
49631     
49632     fileEl: function()
49633     {
49634         return  this.el.select('input',true).first();
49635     },
49636     
49637     canvasEl: function()
49638     {
49639         return this.el.select('canvas',true).first();
49640     },
49641     
49642     canvasElCtx: function()
49643     {
49644         return this.el.select('canvas',true).first().dom.getContext('2d');
49645     },
49646     
49647     getImage: function(type)
49648     {
49649         if(this.is_empty) {
49650             return false;
49651         }
49652         
49653         // encryption ?
49654         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49655     },
49656     
49657     drawFromImage: function(img_src)
49658     {
49659         var img = new Image();
49660         
49661         img.onload = function(){
49662             this.canvasElCtx().drawImage(img, 0, 0);
49663         }.bind(this);
49664         
49665         img.src = img_src;
49666         
49667         this.is_empty = false;
49668     },
49669     
49670     selectImage: function()
49671     {
49672         this.fileEl().dom.click();
49673     },
49674     
49675     uploadImage: function(e)
49676     {
49677         var reader = new FileReader();
49678         
49679         reader.onload = function(e){
49680             var img = new Image();
49681             img.onload = function(){
49682                 this.reset();
49683                 this.canvasElCtx().drawImage(img, 0, 0);
49684             }.bind(this);
49685             img.src = e.target.result;
49686         }.bind(this);
49687         
49688         reader.readAsDataURL(e.target.files[0]);
49689     },
49690     
49691     // Bezier Point Constructor
49692     Point: (function () {
49693         function Point(x, y, time) {
49694             this.x = x;
49695             this.y = y;
49696             this.time = time || Date.now();
49697         }
49698         Point.prototype.distanceTo = function (start) {
49699             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49700         };
49701         Point.prototype.equals = function (other) {
49702             return this.x === other.x && this.y === other.y && this.time === other.time;
49703         };
49704         Point.prototype.velocityFrom = function (start) {
49705             return this.time !== start.time
49706             ? this.distanceTo(start) / (this.time - start.time)
49707             : 0;
49708         };
49709         return Point;
49710     }()),
49711     
49712     
49713     // Bezier Constructor
49714     Bezier: (function () {
49715         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49716             this.startPoint = startPoint;
49717             this.control2 = control2;
49718             this.control1 = control1;
49719             this.endPoint = endPoint;
49720             this.startWidth = startWidth;
49721             this.endWidth = endWidth;
49722         }
49723         Bezier.fromPoints = function (points, widths, scope) {
49724             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49725             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49726             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49727         };
49728         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49729             var dx1 = s1.x - s2.x;
49730             var dy1 = s1.y - s2.y;
49731             var dx2 = s2.x - s3.x;
49732             var dy2 = s2.y - s3.y;
49733             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49734             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49735             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49736             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49737             var dxm = m1.x - m2.x;
49738             var dym = m1.y - m2.y;
49739             var k = l2 / (l1 + l2);
49740             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49741             var tx = s2.x - cm.x;
49742             var ty = s2.y - cm.y;
49743             return {
49744                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49745                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49746             };
49747         };
49748         Bezier.prototype.length = function () {
49749             var steps = 10;
49750             var length = 0;
49751             var px;
49752             var py;
49753             for (var i = 0; i <= steps; i += 1) {
49754                 var t = i / steps;
49755                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49756                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49757                 if (i > 0) {
49758                     var xdiff = cx - px;
49759                     var ydiff = cy - py;
49760                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49761                 }
49762                 px = cx;
49763                 py = cy;
49764             }
49765             return length;
49766         };
49767         Bezier.prototype.point = function (t, start, c1, c2, end) {
49768             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49769             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49770             + (3.0 * c2 * (1.0 - t) * t * t)
49771             + (end * t * t * t);
49772         };
49773         return Bezier;
49774     }()),
49775     
49776     throttleStroke: function(fn, wait) {
49777       if (wait === void 0) { wait = 250; }
49778       var previous = 0;
49779       var timeout = null;
49780       var result;
49781       var storedContext;
49782       var storedArgs;
49783       var later = function () {
49784           previous = Date.now();
49785           timeout = null;
49786           result = fn.apply(storedContext, storedArgs);
49787           if (!timeout) {
49788               storedContext = null;
49789               storedArgs = [];
49790           }
49791       };
49792       return function wrapper() {
49793           var args = [];
49794           for (var _i = 0; _i < arguments.length; _i++) {
49795               args[_i] = arguments[_i];
49796           }
49797           var now = Date.now();
49798           var remaining = wait - (now - previous);
49799           storedContext = this;
49800           storedArgs = args;
49801           if (remaining <= 0 || remaining > wait) {
49802               if (timeout) {
49803                   clearTimeout(timeout);
49804                   timeout = null;
49805               }
49806               previous = now;
49807               result = fn.apply(storedContext, storedArgs);
49808               if (!timeout) {
49809                   storedContext = null;
49810                   storedArgs = [];
49811               }
49812           }
49813           else if (!timeout) {
49814               timeout = window.setTimeout(later, remaining);
49815           }
49816           return result;
49817       };
49818   }
49819   
49820 });
49821
49822  
49823
49824  // old names for form elements
49825 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49826 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49827 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49828 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49829 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49830 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49831 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49832 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49833 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49834 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49835 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49836 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49837 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49838 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49839 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49840 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49841 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49842 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49843 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49844 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49845 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49846 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49847 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49848 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49849 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49850 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49851
49852 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49853 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49854
49855 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49856 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49857
49858 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49859 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49860 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49861 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49862