roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} tooltipClass Class for the tooltip element
240  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
241  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
242  
243  * @constructor
244  * Do not use directly - it does not do anything..
245  * @param {Object} config The config object
246  */
247
248
249
250 Roo.bootstrap.Component = function(config){
251     Roo.bootstrap.Component.superclass.constructor.call(this, config);
252        
253     this.addEvents({
254         /**
255          * @event childrenrendered
256          * Fires when the children have been rendered..
257          * @param {Roo.bootstrap.Component} this
258          */
259         "childrenrendered" : true
260         
261         
262         
263     });
264     
265     
266 };
267
268 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
269     
270     
271     allowDomMove : false, // to stop relocations in parent onRender...
272     
273     cls : false,
274     
275     style : false,
276     
277     autoCreate : false,
278     
279     tooltip : null,
280
281     tooltipClass : null,
282     /**
283      * Initialize Events for the element
284      */
285     initEvents : function() { },
286     
287     xattr : false,
288     
289     parentId : false,
290     
291     can_build_overlaid : true,
292     
293     container_method : false,
294     
295     dataId : false,
296     
297     name : false,
298     
299     parent: function() {
300         // returns the parent component..
301         return Roo.ComponentMgr.get(this.parentId)
302         
303         
304     },
305     
306     // private
307     onRender : function(ct, position)
308     {
309        // Roo.log("Call onRender: " + this.xtype);
310         
311         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
312         
313         if(this.el){
314             if (this.el.attr('xtype')) {
315                 this.el.attr('xtypex', this.el.attr('xtype'));
316                 this.el.dom.removeAttribute('xtype');
317                 
318                 this.initEvents();
319             }
320             
321             return;
322         }
323         
324          
325         
326         var cfg = Roo.apply({},  this.getAutoCreate());
327         
328         cfg.id = this.id || Roo.id();
329         
330         // fill in the extra attributes 
331         if (this.xattr && typeof(this.xattr) =='object') {
332             for (var i in this.xattr) {
333                 cfg[i] = this.xattr[i];
334             }
335         }
336         
337         if(this.dataId){
338             cfg.dataId = this.dataId;
339         }
340         
341         if (this.cls) {
342             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
343         }
344         
345         if (this.style) { // fixme needs to support more complex style data.
346             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347         }
348         
349         if(this.name){
350             cfg.name = this.name;
351         }
352         
353         this.el = ct.createChild(cfg, position);
354         
355         if (this.tooltip) {
356             this.tooltipEl().attr('tooltip', this.tooltip);
357         }
358
359         if (this.tooltipClass) {
360             this.tooltipEl().attr('tooltip-class', this.tooltipClass);
361         }
362         
363         if(this.tabIndex !== undefined){
364             this.el.dom.setAttribute('tabIndex', this.tabIndex);
365         }
366         
367         this.initEvents();
368         
369     },
370     /**
371      * Fetch the element to add children to
372      * @return {Roo.Element} defaults to this.el
373      */
374     getChildContainer : function()
375     {
376         return this.el;
377     },
378     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
379     {
380         return Roo.get(document.body);
381     },
382     
383     /**
384      * Fetch the element to display the tooltip on.
385      * @return {Roo.Element} defaults to this.el
386      */
387     tooltipEl : function()
388     {
389         return this.el;
390     },
391         
392     addxtype  : function(tree,cntr)
393     {
394         var cn = this;
395         
396         cn = Roo.factory(tree);
397         //Roo.log(['addxtype', cn]);
398            
399         cn.parentType = this.xtype; //??
400         cn.parentId = this.id;
401         
402         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
403         if (typeof(cn.container_method) == 'string') {
404             cntr = cn.container_method;
405         }
406         
407         
408         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
409         
410         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
411         
412         var build_from_html =  Roo.XComponent.build_from_html;
413           
414         var is_body  = (tree.xtype == 'Body') ;
415           
416         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
417           
418         var self_cntr_el = Roo.get(this[cntr](false));
419         
420         // do not try and build conditional elements 
421         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
422             return false;
423         }
424         
425         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
426             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
427                 return this.addxtypeChild(tree,cntr, is_body);
428             }
429             
430             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
431                 
432             if(echild){
433                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
434             }
435             
436             Roo.log('skipping render');
437             return cn;
438             
439         }
440         
441         var ret = false;
442         if (!build_from_html) {
443             return false;
444         }
445         
446         // this i think handles overlaying multiple children of the same type
447         // with the sam eelement.. - which might be buggy..
448         while (true) {
449             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
450             
451             if (!echild) {
452                 break;
453             }
454             
455             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
456                 break;
457             }
458             
459             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
460         }
461        
462         return ret;
463     },
464     
465     
466     addxtypeChild : function (tree, cntr, is_body)
467     {
468         Roo.debug && Roo.log('addxtypeChild:' + cntr);
469         var cn = this;
470         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
471         
472         
473         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
474                     (typeof(tree['flexy:foreach']) != 'undefined');
475           
476     
477         
478         skip_children = false;
479         // render the element if it's not BODY.
480         if (!is_body) {
481             
482             // if parent was disabled, then do not try and create the children..
483             if(!this[cntr](true)){
484                 tree.items = [];
485                 return tree;
486             }
487            
488             cn = Roo.factory(tree);
489            
490             cn.parentType = this.xtype; //??
491             cn.parentId = this.id;
492             
493             var build_from_html =  Roo.XComponent.build_from_html;
494             
495             
496             // does the container contain child eleemnts with 'xtype' attributes.
497             // that match this xtype..
498             // note - when we render we create these as well..
499             // so we should check to see if body has xtype set.
500             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
501                
502                 var self_cntr_el = Roo.get(this[cntr](false));
503                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
504                 if (echild) { 
505                     //Roo.log(Roo.XComponent.build_from_html);
506                     //Roo.log("got echild:");
507                     //Roo.log(echild);
508                 }
509                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
510                 // and are not displayed -this causes this to use up the wrong element when matching.
511                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
512                 
513                 
514                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
515                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
516                   
517                   
518                   
519                     cn.el = echild;
520                   //  Roo.log("GOT");
521                     //echild.dom.removeAttribute('xtype');
522                 } else {
523                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
524                     Roo.debug && Roo.log(self_cntr_el);
525                     Roo.debug && Roo.log(echild);
526                     Roo.debug && Roo.log(cn);
527                 }
528             }
529            
530             
531            
532             // if object has flexy:if - then it may or may not be rendered.
533             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
534                 // skip a flexy if element.
535                 Roo.debug && Roo.log('skipping render');
536                 Roo.debug && Roo.log(tree);
537                 if (!cn.el) {
538                     Roo.debug && Roo.log('skipping all children');
539                     skip_children = true;
540                 }
541                 
542              } else {
543                  
544                 // actually if flexy:foreach is found, we really want to create 
545                 // multiple copies here...
546                 //Roo.log('render');
547                 //Roo.log(this[cntr]());
548                 // some elements do not have render methods.. like the layouts...
549                 /*
550                 if(this[cntr](true) === false){
551                     cn.items = [];
552                     return cn;
553                 }
554                 */
555                 cn.render && cn.render(this[cntr](true));
556                 
557              }
558             // then add the element..
559         }
560          
561         // handle the kids..
562         
563         var nitems = [];
564         /*
565         if (typeof (tree.menu) != 'undefined') {
566             tree.menu.parentType = cn.xtype;
567             tree.menu.triggerEl = cn.el;
568             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
569             
570         }
571         */
572         if (!tree.items || !tree.items.length) {
573             cn.items = nitems;
574             //Roo.log(["no children", this]);
575             
576             return cn;
577         }
578          
579         var items = tree.items;
580         delete tree.items;
581         
582         //Roo.log(items.length);
583             // add the items..
584         if (!skip_children) {    
585             for(var i =0;i < items.length;i++) {
586               //  Roo.log(['add child', items[i]]);
587                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
588             }
589         }
590         
591         cn.items = nitems;
592         
593         //Roo.log("fire childrenrendered");
594         
595         cn.fireEvent('childrenrendered', this);
596         
597         return cn;
598     },
599     
600     /**
601      * Set the element that will be used to show or hide
602      */
603     setVisibilityEl : function(el)
604     {
605         this.visibilityEl = el;
606     },
607     
608      /**
609      * Get the element that will be used to show or hide
610      */
611     getVisibilityEl : function()
612     {
613         if (typeof(this.visibilityEl) == 'object') {
614             return this.visibilityEl;
615         }
616         
617         if (typeof(this.visibilityEl) == 'string') {
618             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
619         }
620         
621         return this.getEl();
622     },
623     
624     /**
625      * Show a component - removes 'hidden' class
626      */
627     show : function()
628     {
629         if(!this.getVisibilityEl()){
630             return;
631         }
632          
633         this.getVisibilityEl().removeClass(['hidden','d-none']);
634         
635         this.fireEvent('show', this);
636         
637         
638     },
639     /**
640      * Hide a component - adds 'hidden' class
641      */
642     hide: function()
643     {
644         if(!this.getVisibilityEl()){
645             return;
646         }
647         
648         this.getVisibilityEl().addClass(['hidden','d-none']);
649         
650         this.fireEvent('hide', this);
651         
652     }
653 });
654
655  /*
656  * - LGPL
657  *
658  * element
659  * 
660  */
661
662 /**
663  * @class Roo.bootstrap.Element
664  * @extends Roo.bootstrap.Component
665  * @children Roo.bootstrap.Component
666  * Bootstrap Element class (basically a DIV used to make random stuff )
667  * 
668  * @cfg {String} html contents of the element
669  * @cfg {String} tag tag of the element
670  * @cfg {String} cls class of the element
671  * @cfg {Boolean} preventDefault (true|false) default false
672  * @cfg {Boolean} clickable (true|false) default false
673  * @cfg {String} role default blank - set to button to force cursor pointer
674  
675  * 
676  * @constructor
677  * Create a new Element
678  * @param {Object} config The config object
679  */
680
681 Roo.bootstrap.Element = function(config){
682     Roo.bootstrap.Element.superclass.constructor.call(this, config);
683     
684     this.addEvents({
685         // raw events
686         /**
687          * @event click
688          * When a element is chick
689          * @param {Roo.bootstrap.Element} this
690          * @param {Roo.EventObject} e
691          */
692         "click" : true 
693         
694       
695     });
696 };
697
698 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
699     
700     tag: 'div',
701     cls: '',
702     html: '',
703     preventDefault: false, 
704     clickable: false,
705     tapedTwice : false,
706     role : false,
707     
708     getAutoCreate : function(){
709         
710         var cfg = {
711             tag: this.tag,
712             // cls: this.cls, double assign in parent class Component.js :: onRender
713             html: this.html
714         };
715         if (this.role !== false) {
716             cfg.role = this.role;
717         }
718         
719         return cfg;
720     },
721     
722     initEvents: function() 
723     {
724         Roo.bootstrap.Element.superclass.initEvents.call(this);
725         
726         if(this.clickable){
727             this.el.on('click', this.onClick, this);
728         }
729         
730         
731     },
732     
733     onClick : function(e)
734     {
735         if(this.preventDefault){
736             e.preventDefault();
737         }
738         
739         this.fireEvent('click', this, e); // why was this double click before?
740     },
741     
742     
743     
744
745     
746     
747     getValue : function()
748     {
749         return this.el.dom.innerHTML;
750     },
751     
752     setValue : function(value)
753     {
754         this.el.dom.innerHTML = value;
755     }
756    
757 });
758
759  
760
761  /*
762  * - LGPL
763  *
764  * dropable area
765  * 
766  */
767
768 /**
769  * @class Roo.bootstrap.DropTarget
770  * @extends Roo.bootstrap.Element
771  * Bootstrap DropTarget class
772  
773  * @cfg {string} name dropable name
774  * 
775  * @constructor
776  * Create a new Dropable Area
777  * @param {Object} config The config object
778  */
779
780 Roo.bootstrap.DropTarget = function(config){
781     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
782     
783     this.addEvents({
784         // raw events
785         /**
786          * @event click
787          * When a element is chick
788          * @param {Roo.bootstrap.Element} this
789          * @param {Roo.EventObject} e
790          */
791         "drop" : true
792     });
793 };
794
795 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
796     
797     
798     getAutoCreate : function(){
799         
800          
801     },
802     
803     initEvents: function() 
804     {
805         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
806         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
807             ddGroup: this.name,
808             listeners : {
809                 drop : this.dragDrop.createDelegate(this),
810                 enter : this.dragEnter.createDelegate(this),
811                 out : this.dragOut.createDelegate(this),
812                 over : this.dragOver.createDelegate(this)
813             }
814             
815         });
816         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
817     },
818     
819     dragDrop : function(source,e,data)
820     {
821         // user has to decide how to impliment this.
822         Roo.log('drop');
823         Roo.log(this);
824         //this.fireEvent('drop', this, source, e ,data);
825         return false;
826     },
827     
828     dragEnter : function(n, dd, e, data)
829     {
830         // probably want to resize the element to match the dropped element..
831         Roo.log("enter");
832         this.originalSize = this.el.getSize();
833         this.el.setSize( n.el.getSize());
834         this.dropZone.DDM.refreshCache(this.name);
835         Roo.log([n, dd, e, data]);
836     },
837     
838     dragOut : function(value)
839     {
840         // resize back to normal
841         Roo.log("out");
842         this.el.setSize(this.originalSize);
843         this.dropZone.resetConstraints();
844     },
845     
846     dragOver : function()
847     {
848         // ??? do nothing?
849     }
850    
851 });
852
853  
854
855  /*
856  * - LGPL
857  *
858  * Body
859  *
860  */
861
862 /**
863  * @class Roo.bootstrap.Body
864  * @extends Roo.bootstrap.Component
865  * @children Roo.bootstrap.Component 
866  * @parent none builder
867  * Bootstrap Body class
868  *
869  * @constructor
870  * Create a new body
871  * @param {Object} config The config object
872  */
873
874 Roo.bootstrap.Body = function(config){
875
876     config = config || {};
877
878     Roo.bootstrap.Body.superclass.constructor.call(this, config);
879     this.el = Roo.get(config.el ? config.el : document.body );
880     if (this.cls && this.cls.length) {
881         Roo.get(document.body).addClass(this.cls);
882     }
883 };
884
885 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
886
887     is_body : true,// just to make sure it's constructed?
888
889         autoCreate : {
890         cls: 'container'
891     },
892     onRender : function(ct, position)
893     {
894        /* Roo.log("Roo.bootstrap.Body - onRender");
895         if (this.cls && this.cls.length) {
896             Roo.get(document.body).addClass(this.cls);
897         }
898         // style??? xttr???
899         */
900     }
901
902
903
904
905 });
906 /*
907  * - LGPL
908  *
909  * button group
910  * 
911  */
912
913
914 /**
915  * @class Roo.bootstrap.ButtonGroup
916  * @extends Roo.bootstrap.Component
917  * Bootstrap ButtonGroup class
918  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
919  * 
920  * @cfg {String} size lg | sm | xs (default empty normal)
921  * @cfg {String} align vertical | justified  (default none)
922  * @cfg {String} direction up | down (default down)
923  * @cfg {Boolean} toolbar false | true
924  * @cfg {Boolean} btn true | false
925  * 
926  * 
927  * @constructor
928  * Create a new Input
929  * @param {Object} config The config object
930  */
931
932 Roo.bootstrap.ButtonGroup = function(config){
933     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
934 };
935
936 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
937     
938     size: '',
939     align: '',
940     direction: '',
941     toolbar: false,
942     btn: true,
943
944     getAutoCreate : function(){
945         var cfg = {
946             cls: 'btn-group',
947             html : null
948         };
949         
950         cfg.html = this.html || cfg.html;
951         
952         if (this.toolbar) {
953             cfg = {
954                 cls: 'btn-toolbar',
955                 html: null
956             };
957             
958             return cfg;
959         }
960         
961         if (['vertical','justified'].indexOf(this.align)!==-1) {
962             cfg.cls = 'btn-group-' + this.align;
963             
964             if (this.align == 'justified') {
965                 console.log(this.items);
966             }
967         }
968         
969         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
970             cfg.cls += ' btn-group-' + this.size;
971         }
972         
973         if (this.direction == 'up') {
974             cfg.cls += ' dropup' ;
975         }
976         
977         return cfg;
978     },
979     /**
980      * Add a button to the group (similar to NavItem API.)
981      */
982     addItem : function(cfg)
983     {
984         var cn = new Roo.bootstrap.Button(cfg);
985         //this.register(cn);
986         cn.parentId = this.id;
987         cn.onRender(this.el, null);
988         return cn;
989     }
990    
991 });
992
993  /*
994  * - LGPL
995  *
996  * button
997  * 
998  */
999
1000 /**
1001  * @class Roo.bootstrap.Button
1002  * @extends Roo.bootstrap.Component
1003  * Bootstrap Button class
1004  * @cfg {String} html The button content
1005  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1006  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1007  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1008  * @cfg {String} size (lg|sm|xs)
1009  * @cfg {String} tag (a|input|submit)
1010  * @cfg {String} href empty or href
1011  * @cfg {Boolean} disabled default false;
1012  * @cfg {Boolean} isClose default false;
1013  * @cfg {String} glyphicon depricated - use fa
1014  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1015  * @cfg {String} badge text for badge
1016  * @cfg {String} theme (default|glow)  
1017  * @cfg {Boolean} inverse dark themed version
1018  * @cfg {Boolean} toggle is it a slidy toggle button
1019  * @cfg {Boolean} pressed   default null - if the button ahs active state
1020  * @cfg {String} ontext text for on slidy toggle state
1021  * @cfg {String} offtext text for off slidy toggle state
1022  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1023  * @cfg {Boolean} removeClass remove the standard class..
1024  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1025  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1026  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1027
1028  * @constructor
1029  * Create a new button
1030  * @param {Object} config The config object
1031  */
1032
1033
1034 Roo.bootstrap.Button = function(config){
1035     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1036     
1037     this.addEvents({
1038         // raw events
1039         /**
1040          * @event click
1041          * When a button is pressed
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "click" : true,
1046         /**
1047          * @event dblclick
1048          * When a button is double clicked
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          */
1052         "dblclick" : true,
1053          /**
1054          * @event toggle
1055          * After the button has been toggles
1056          * @param {Roo.bootstrap.Button} btn
1057          * @param {Roo.EventObject} e
1058          * @param {boolean} pressed (also available as button.pressed)
1059          */
1060         "toggle" : true
1061     });
1062 };
1063
1064 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1065     html: false,
1066     active: false,
1067     weight: '',
1068     badge_weight: '',
1069     outline : false,
1070     size: '',
1071     tag: 'button',
1072     href: '',
1073     disabled: false,
1074     isClose: false,
1075     glyphicon: '',
1076     fa: '',
1077     badge: '',
1078     theme: 'default',
1079     inverse: false,
1080     
1081     toggle: false,
1082     ontext: 'ON',
1083     offtext: 'OFF',
1084     defaulton: true,
1085     preventDefault: true,
1086     removeClass: false,
1087     name: false,
1088     target: false,
1089     group : false,
1090      
1091     pressed : null,
1092      
1093     
1094     getAutoCreate : function(){
1095         
1096         var cfg = {
1097             tag : 'button',
1098             cls : 'roo-button',
1099             html: ''
1100         };
1101         
1102         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1103             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1104             this.tag = 'button';
1105         } else {
1106             cfg.tag = this.tag;
1107         }
1108         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1109         
1110         if (this.toggle == true) {
1111             cfg={
1112                 tag: 'div',
1113                 cls: 'slider-frame roo-button',
1114                 cn: [
1115                     {
1116                         tag: 'span',
1117                         'data-on-text':'ON',
1118                         'data-off-text':'OFF',
1119                         cls: 'slider-button',
1120                         html: this.offtext
1121                     }
1122                 ]
1123             };
1124             // why are we validating the weights?
1125             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126                 cfg.cls +=  ' ' + this.weight;
1127             }
1128             
1129             return cfg;
1130         }
1131         
1132         if (this.isClose) {
1133             cfg.cls += ' close';
1134             
1135             cfg["aria-hidden"] = true;
1136             
1137             cfg.html = "&times;";
1138             
1139             return cfg;
1140         }
1141              
1142         
1143         if (this.theme==='default') {
1144             cfg.cls = 'btn roo-button';
1145             
1146             //if (this.parentType != 'Navbar') {
1147             this.weight = this.weight.length ?  this.weight : 'default';
1148             //}
1149             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1150                 
1151                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1152                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1153                 cfg.cls += ' btn-' + outline + weight;
1154                 if (this.weight == 'default') {
1155                     // BC
1156                     cfg.cls += ' btn-' + this.weight;
1157                 }
1158             }
1159         } else if (this.theme==='glow') {
1160             
1161             cfg.tag = 'a';
1162             cfg.cls = 'btn-glow roo-button';
1163             
1164             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1165                 
1166                 cfg.cls += ' ' + this.weight;
1167             }
1168         }
1169    
1170         
1171         if (this.inverse) {
1172             this.cls += ' inverse';
1173         }
1174         
1175         
1176         if (this.active || this.pressed === true) {
1177             cfg.cls += ' active';
1178         }
1179         
1180         if (this.disabled) {
1181             cfg.disabled = 'disabled';
1182         }
1183         
1184         if (this.items) {
1185             Roo.log('changing to ul' );
1186             cfg.tag = 'ul';
1187             this.glyphicon = 'caret';
1188             if (Roo.bootstrap.version == 4) {
1189                 this.fa = 'caret-down';
1190             }
1191             
1192         }
1193         
1194         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1195          
1196         //gsRoo.log(this.parentType);
1197         if (this.parentType === 'Navbar' && !this.parent().bar) {
1198             Roo.log('changing to li?');
1199             
1200             cfg.tag = 'li';
1201             
1202             cfg.cls = '';
1203             cfg.cn =  [{
1204                 tag : 'a',
1205                 cls : 'roo-button',
1206                 html : this.html,
1207                 href : this.href || '#'
1208             }];
1209             if (this.menu) {
1210                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1211                 cfg.cls += ' dropdown';
1212             }   
1213             
1214             delete cfg.html;
1215             
1216         }
1217         
1218        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1219         
1220         if (this.glyphicon) {
1221             cfg.html = ' ' + cfg.html;
1222             
1223             cfg.cn = [
1224                 {
1225                     tag: 'span',
1226                     cls: 'glyphicon glyphicon-' + this.glyphicon
1227                 }
1228             ];
1229         }
1230         if (this.fa) {
1231             cfg.html = ' ' + cfg.html;
1232             
1233             cfg.cn = [
1234                 {
1235                     tag: 'i',
1236                     cls: 'fa fas fa-' + this.fa
1237                 }
1238             ];
1239         }
1240         
1241         if (this.badge) {
1242             cfg.html += ' ';
1243             
1244             cfg.tag = 'a';
1245             
1246 //            cfg.cls='btn roo-button';
1247             
1248             cfg.href=this.href;
1249             
1250             var value = cfg.html;
1251             
1252             if(this.glyphicon){
1253                 value = {
1254                     tag: 'span',
1255                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1256                     html: this.html
1257                 };
1258             }
1259             if(this.fa){
1260                 value = {
1261                     tag: 'i',
1262                     cls: 'fa fas fa-' + this.fa,
1263                     html: this.html
1264                 };
1265             }
1266             
1267             var bw = this.badge_weight.length ? this.badge_weight :
1268                 (this.weight.length ? this.weight : 'secondary');
1269             bw = bw == 'default' ? 'secondary' : bw;
1270             
1271             cfg.cn = [
1272                 value,
1273                 {
1274                     tag: 'span',
1275                     cls: 'badge badge-' + bw,
1276                     html: this.badge
1277                 }
1278             ];
1279             
1280             cfg.html='';
1281         }
1282         
1283         if (this.menu) {
1284             cfg.cls += ' dropdown';
1285             cfg.html = typeof(cfg.html) != 'undefined' ?
1286                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1287         }
1288         
1289         if (cfg.tag !== 'a' && this.href !== '') {
1290             throw "Tag must be a to set href.";
1291         } else if (this.href.length > 0) {
1292             cfg.href = this.href;
1293         }
1294         
1295         if(this.removeClass){
1296             cfg.cls = '';
1297         }
1298         
1299         if(this.target){
1300             cfg.target = this.target;
1301         }
1302         
1303         return cfg;
1304     },
1305     initEvents: function() {
1306        // Roo.log('init events?');
1307 //        Roo.log(this.el.dom);
1308         // add the menu...
1309         
1310         if (typeof (this.menu) != 'undefined') {
1311             this.menu.parentType = this.xtype;
1312             this.menu.triggerEl = this.el;
1313             this.addxtype(Roo.apply({}, this.menu));
1314         }
1315
1316
1317         if (this.el.hasClass('roo-button')) {
1318              this.el.on('click', this.onClick, this);
1319              this.el.on('dblclick', this.onDblClick, this);
1320         } else {
1321              this.el.select('.roo-button').on('click', this.onClick, this);
1322              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1323              
1324         }
1325         // why?
1326         if(this.removeClass){
1327             this.el.on('click', this.onClick, this);
1328         }
1329         
1330         if (this.group === true) {
1331              if (this.pressed === false || this.pressed === true) {
1332                 // nothing
1333             } else {
1334                 this.pressed = false;
1335                 this.setActive(this.pressed);
1336             }
1337             
1338         }
1339         
1340         this.el.enableDisplayMode();
1341         
1342     },
1343     onClick : function(e)
1344     {
1345         if (this.disabled) {
1346             return;
1347         }
1348         
1349         Roo.log('button on click ');
1350         if(this.href === '' || this.preventDefault){
1351             e.preventDefault();
1352         }
1353         
1354         if (this.group) {
1355             if (this.pressed) {
1356                 // do nothing -
1357                 return;
1358             }
1359             this.setActive(true);
1360             var pi = this.parent().items;
1361             for (var i = 0;i < pi.length;i++) {
1362                 if (this == pi[i]) {
1363                     continue;
1364                 }
1365                 if (pi[i].el.hasClass('roo-button')) {
1366                     pi[i].setActive(false);
1367                 }
1368             }
1369             this.fireEvent('click', this, e);            
1370             return;
1371         }
1372         
1373         if (this.pressed === true || this.pressed === false) {
1374             this.toggleActive(e);
1375         }
1376         
1377         
1378         this.fireEvent('click', this, e);
1379     },
1380     onDblClick: function(e)
1381     {
1382         if (this.disabled) {
1383             return;
1384         }
1385         if(this.preventDefault){
1386             e.preventDefault();
1387         }
1388         this.fireEvent('dblclick', this, e);
1389     },
1390     /**
1391      * Enables this button
1392      */
1393     enable : function()
1394     {
1395         this.disabled = false;
1396         this.el.removeClass('disabled');
1397         this.el.dom.removeAttribute("disabled");
1398     },
1399     
1400     /**
1401      * Disable this button
1402      */
1403     disable : function()
1404     {
1405         this.disabled = true;
1406         this.el.addClass('disabled');
1407         this.el.attr("disabled", "disabled")
1408     },
1409      /**
1410      * sets the active state on/off, 
1411      * @param {Boolean} state (optional) Force a particular state
1412      */
1413     setActive : function(v) {
1414         
1415         this.el[v ? 'addClass' : 'removeClass']('active');
1416         this.pressed = v;
1417     },
1418      /**
1419      * toggles the current active state 
1420      */
1421     toggleActive : function(e)
1422     {
1423         this.setActive(!this.pressed); // this modifies pressed...
1424         this.fireEvent('toggle', this, e, this.pressed);
1425     },
1426      /**
1427      * get the current active state
1428      * @return {boolean} true if it's active
1429      */
1430     isActive : function()
1431     {
1432         return this.el.hasClass('active');
1433     },
1434     /**
1435      * set the text of the first selected button
1436      */
1437     setText : function(str)
1438     {
1439         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1440     },
1441     /**
1442      * get the text of the first selected button
1443      */
1444     getText : function()
1445     {
1446         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1447     },
1448     
1449     setWeight : function(str)
1450     {
1451         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1452         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1453         this.weight = str;
1454         var outline = this.outline ? 'outline-' : '';
1455         if (str == 'default') {
1456             this.el.addClass('btn-default btn-outline-secondary');        
1457             return;
1458         }
1459         this.el.addClass('btn-' + outline + str);        
1460     }
1461     
1462     
1463 });
1464 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1465
1466 Roo.bootstrap.Button.weights = [
1467     'default',
1468     'secondary' ,
1469     'primary',
1470     'success',
1471     'info',
1472     'warning',
1473     'danger',
1474     'link',
1475     'light',
1476     'dark'              
1477    
1478 ];/*
1479  * - LGPL
1480  *
1481  * column
1482  * 
1483  */
1484
1485 /**
1486  * @class Roo.bootstrap.Column
1487  * @extends Roo.bootstrap.Component
1488  * @children Roo.bootstrap.Component
1489  * Bootstrap Column class
1490  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1491  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1492  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1493  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1494  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1495  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1496  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1497  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1498  *
1499  * 
1500  * @cfg {Boolean} hidden (true|false) hide the element
1501  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1502  * @cfg {String} fa (ban|check|...) font awesome icon
1503  * @cfg {Number} fasize (1|2|....) font awsome size
1504
1505  * @cfg {String} icon (info-sign|check|...) glyphicon name
1506
1507  * @cfg {String} html content of column.
1508  * 
1509  * @constructor
1510  * Create a new Column
1511  * @param {Object} config The config object
1512  */
1513
1514 Roo.bootstrap.Column = function(config){
1515     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1516 };
1517
1518 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1519     
1520     xs: false,
1521     sm: false,
1522     md: false,
1523     lg: false,
1524     xsoff: false,
1525     smoff: false,
1526     mdoff: false,
1527     lgoff: false,
1528     html: '',
1529     offset: 0,
1530     alert: false,
1531     fa: false,
1532     icon : false,
1533     hidden : false,
1534     fasize : 1,
1535     
1536     getAutoCreate : function(){
1537         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538         
1539         cfg = {
1540             tag: 'div',
1541             cls: 'column'
1542         };
1543         
1544         var settings=this;
1545         var sizes =   ['xs','sm','md','lg'];
1546         sizes.map(function(size ,ix){
1547             //Roo.log( size + ':' + settings[size]);
1548             
1549             if (settings[size+'off'] !== false) {
1550                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1551             }
1552             
1553             if (settings[size] === false) {
1554                 return;
1555             }
1556             
1557             if (!settings[size]) { // 0 = hidden
1558                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1559                 // bootsrap4
1560                 for (var i = ix; i > -1; i--) {
1561                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1562                 }
1563                 
1564                 
1565                 return;
1566             }
1567             cfg.cls += ' col-' + size + '-' + settings[size] + (
1568                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1569             );
1570             
1571         });
1572         
1573         if (this.hidden) {
1574             cfg.cls += ' hidden';
1575         }
1576         
1577         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1578             cfg.cls +=' alert alert-' + this.alert;
1579         }
1580         
1581         
1582         if (this.html.length) {
1583             cfg.html = this.html;
1584         }
1585         if (this.fa) {
1586             var fasize = '';
1587             if (this.fasize > 1) {
1588                 fasize = ' fa-' + this.fasize + 'x';
1589             }
1590             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1591             
1592             
1593         }
1594         if (this.icon) {
1595             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1596         }
1597         
1598         return cfg;
1599     }
1600    
1601 });
1602
1603  
1604
1605  /*
1606  * - LGPL
1607  *
1608  * page container.
1609  * 
1610  */
1611
1612
1613 /**
1614  * @class Roo.bootstrap.Container
1615  * @extends Roo.bootstrap.Component
1616  * @children Roo.bootstrap.Component
1617  * @parent builder
1618  * Bootstrap Container class
1619  * @cfg {Boolean} jumbotron is it a jumbotron element
1620  * @cfg {String} html content of element
1621  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1622  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1623  * @cfg {String} header content of header (for panel)
1624  * @cfg {String} footer content of footer (for panel)
1625  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1626  * @cfg {String} tag (header|aside|section) type of HTML tag.
1627  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1628  * @cfg {String} fa font awesome icon
1629  * @cfg {String} icon (info-sign|check|...) glyphicon name
1630  * @cfg {Boolean} hidden (true|false) hide the element
1631  * @cfg {Boolean} expandable (true|false) default false
1632  * @cfg {Boolean} expanded (true|false) default true
1633  * @cfg {String} rheader contet on the right of header
1634  * @cfg {Boolean} clickable (true|false) default false
1635
1636  *     
1637  * @constructor
1638  * Create a new Container
1639  * @param {Object} config The config object
1640  */
1641
1642 Roo.bootstrap.Container = function(config){
1643     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1644     
1645     this.addEvents({
1646         // raw events
1647          /**
1648          * @event expand
1649          * After the panel has been expand
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "expand" : true,
1654         /**
1655          * @event collapse
1656          * After the panel has been collapsed
1657          * 
1658          * @param {Roo.bootstrap.Container} this
1659          */
1660         "collapse" : true,
1661         /**
1662          * @event click
1663          * When a element is chick
1664          * @param {Roo.bootstrap.Container} this
1665          * @param {Roo.EventObject} e
1666          */
1667         "click" : true
1668     });
1669 };
1670
1671 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1672     
1673     jumbotron : false,
1674     well: '',
1675     panel : '',
1676     header: '',
1677     footer : '',
1678     sticky: '',
1679     tag : false,
1680     alert : false,
1681     fa: false,
1682     icon : false,
1683     expandable : false,
1684     rheader : '',
1685     expanded : true,
1686     clickable: false,
1687   
1688      
1689     getChildContainer : function() {
1690         
1691         if(!this.el){
1692             return false;
1693         }
1694         
1695         if (this.panel.length) {
1696             return this.el.select('.panel-body',true).first();
1697         }
1698         
1699         return this.el;
1700     },
1701     
1702     
1703     getAutoCreate : function(){
1704         
1705         var cfg = {
1706             tag : this.tag || 'div',
1707             html : '',
1708             cls : ''
1709         };
1710         if (this.jumbotron) {
1711             cfg.cls = 'jumbotron';
1712         }
1713         
1714         
1715         
1716         // - this is applied by the parent..
1717         //if (this.cls) {
1718         //    cfg.cls = this.cls + '';
1719         //}
1720         
1721         if (this.sticky.length) {
1722             
1723             var bd = Roo.get(document.body);
1724             if (!bd.hasClass('bootstrap-sticky')) {
1725                 bd.addClass('bootstrap-sticky');
1726                 Roo.select('html',true).setStyle('height', '100%');
1727             }
1728              
1729             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1730         }
1731         
1732         
1733         if (this.well.length) {
1734             switch (this.well) {
1735                 case 'lg':
1736                 case 'sm':
1737                     cfg.cls +=' well well-' +this.well;
1738                     break;
1739                 default:
1740                     cfg.cls +=' well';
1741                     break;
1742             }
1743         }
1744         
1745         if (this.hidden) {
1746             cfg.cls += ' hidden';
1747         }
1748         
1749         
1750         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1751             cfg.cls +=' alert alert-' + this.alert;
1752         }
1753         
1754         var body = cfg;
1755         
1756         if (this.panel.length) {
1757             cfg.cls += ' panel panel-' + this.panel;
1758             cfg.cn = [];
1759             if (this.header.length) {
1760                 
1761                 var h = [];
1762                 
1763                 if(this.expandable){
1764                     
1765                     cfg.cls = cfg.cls + ' expandable';
1766                     
1767                     h.push({
1768                         tag: 'i',
1769                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1770                     });
1771                     
1772                 }
1773                 
1774                 h.push(
1775                     {
1776                         tag: 'span',
1777                         cls : 'panel-title',
1778                         html : (this.expandable ? '&nbsp;' : '') + this.header
1779                     },
1780                     {
1781                         tag: 'span',
1782                         cls: 'panel-header-right',
1783                         html: this.rheader
1784                     }
1785                 );
1786                 
1787                 cfg.cn.push({
1788                     cls : 'panel-heading',
1789                     style : this.expandable ? 'cursor: pointer' : '',
1790                     cn : h
1791                 });
1792                 
1793             }
1794             
1795             body = false;
1796             cfg.cn.push({
1797                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1798                 html : this.html
1799             });
1800             
1801             
1802             if (this.footer.length) {
1803                 cfg.cn.push({
1804                     cls : 'panel-footer',
1805                     html : this.footer
1806                     
1807                 });
1808             }
1809             
1810         }
1811         
1812         if (body) {
1813             body.html = this.html || cfg.html;
1814             // prefix with the icons..
1815             if (this.fa) {
1816                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1817             }
1818             if (this.icon) {
1819                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1820             }
1821             
1822             
1823         }
1824         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1825             cfg.cls =  'container';
1826         }
1827         
1828         return cfg;
1829     },
1830     
1831     initEvents: function() 
1832     {
1833         if(this.expandable){
1834             var headerEl = this.headerEl();
1835         
1836             if(headerEl){
1837                 headerEl.on('click', this.onToggleClick, this);
1838             }
1839         }
1840         
1841         if(this.clickable){
1842             this.el.on('click', this.onClick, this);
1843         }
1844         
1845     },
1846     
1847     onToggleClick : function()
1848     {
1849         var headerEl = this.headerEl();
1850         
1851         if(!headerEl){
1852             return;
1853         }
1854         
1855         if(this.expanded){
1856             this.collapse();
1857             return;
1858         }
1859         
1860         this.expand();
1861     },
1862     
1863     expand : function()
1864     {
1865         if(this.fireEvent('expand', this)) {
1866             
1867             this.expanded = true;
1868             
1869             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1870             
1871             this.el.select('.panel-body',true).first().removeClass('hide');
1872             
1873             var toggleEl = this.toggleEl();
1874
1875             if(!toggleEl){
1876                 return;
1877             }
1878
1879             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1880         }
1881         
1882     },
1883     
1884     collapse : function()
1885     {
1886         if(this.fireEvent('collapse', this)) {
1887             
1888             this.expanded = false;
1889             
1890             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1891             this.el.select('.panel-body',true).first().addClass('hide');
1892         
1893             var toggleEl = this.toggleEl();
1894
1895             if(!toggleEl){
1896                 return;
1897             }
1898
1899             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1900         }
1901     },
1902     
1903     toggleEl : function()
1904     {
1905         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-heading .fa',true).first();
1910     },
1911     
1912     headerEl : function()
1913     {
1914         if(!this.el || !this.panel.length || !this.header.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-heading',true).first()
1919     },
1920     
1921     bodyEl : function()
1922     {
1923         if(!this.el || !this.panel.length){
1924             return;
1925         }
1926         
1927         return this.el.select('.panel-body',true).first()
1928     },
1929     
1930     titleEl : function()
1931     {
1932         if(!this.el || !this.panel.length || !this.header.length){
1933             return;
1934         }
1935         
1936         return this.el.select('.panel-title',true).first();
1937     },
1938     
1939     setTitle : function(v)
1940     {
1941         var titleEl = this.titleEl();
1942         
1943         if(!titleEl){
1944             return;
1945         }
1946         
1947         titleEl.dom.innerHTML = v;
1948     },
1949     
1950     getTitle : function()
1951     {
1952         
1953         var titleEl = this.titleEl();
1954         
1955         if(!titleEl){
1956             return '';
1957         }
1958         
1959         return titleEl.dom.innerHTML;
1960     },
1961     
1962     setRightTitle : function(v)
1963     {
1964         var t = this.el.select('.panel-header-right',true).first();
1965         
1966         if(!t){
1967             return;
1968         }
1969         
1970         t.dom.innerHTML = v;
1971     },
1972     
1973     onClick : function(e)
1974     {
1975         e.preventDefault();
1976         
1977         this.fireEvent('click', this, e);
1978     }
1979 });
1980
1981  /**
1982  * @class Roo.bootstrap.Card
1983  * @extends Roo.bootstrap.Component
1984  * @children Roo.bootstrap.Component
1985  * @licence LGPL
1986  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1987  *
1988  *
1989  * possible... may not be implemented..
1990  * @cfg {String} header_image  src url of image.
1991  * @cfg {String|Object} header
1992  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1993  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1994  * 
1995  * @cfg {String} title
1996  * @cfg {String} subtitle
1997  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1998  * @cfg {String} footer
1999  
2000  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2001  * 
2002  * @cfg {String} margin (0|1|2|3|4|5|auto)
2003  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2004  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2005  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2006  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2007  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2008  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2009  *
2010  * @cfg {String} padding (0|1|2|3|4|5)
2011  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2012  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2013  * @cfg {String} padding_left (0|1|2|3|4|5)
2014  * @cfg {String} padding_right (0|1|2|3|4|5)
2015  * @cfg {String} padding_x (0|1|2|3|4|5)
2016  * @cfg {String} padding_y (0|1|2|3|4|5)
2017  *
2018  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2019  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2020  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2021  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2022  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023  
2024  * @config {Boolean} dragable  if this card can be dragged.
2025  * @config {String} drag_group  group for drag
2026  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2027  * @config {String} drop_group  group for drag
2028  * 
2029  * @config {Boolean} collapsable can the body be collapsed.
2030  * @config {Boolean} collapsed is the body collapsed when rendered...
2031  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2032  * @config {Boolean} rotated is the body rotated when rendered...
2033  * 
2034  * @constructor
2035  * Create a new Container
2036  * @param {Object} config The config object
2037  */
2038
2039 Roo.bootstrap.Card = function(config){
2040     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2041     
2042     this.addEvents({
2043          // raw events
2044         /**
2045          * @event drop
2046          * When a element a card is dropped
2047          * @param {Roo.bootstrap.Card} this
2048          *
2049          * 
2050          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2051          * @param {String} position 'above' or 'below'
2052          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2053         
2054          */
2055         'drop' : true,
2056          /**
2057          * @event rotate
2058          * When a element a card is rotate
2059          * @param {Roo.bootstrap.Card} this
2060          * @param {Roo.Element} n the node being dropped?
2061          * @param {Boolean} rotate status
2062          */
2063         'rotate' : true,
2064         /**
2065          * @event cardover
2066          * When a card element is dragged over ready to drop (return false to block dropable)
2067          * @param {Roo.bootstrap.Card} this
2068          * @param {Object} data from dragdrop 
2069          */
2070          'cardover' : true
2071          
2072     });
2073 };
2074
2075
2076 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2077     
2078     
2079     weight : '',
2080     
2081     margin: '', /// may be better in component?
2082     margin_top: '', 
2083     margin_bottom: '', 
2084     margin_left: '',
2085     margin_right: '',
2086     margin_x: '',
2087     margin_y: '',
2088     
2089     padding : '',
2090     padding_top: '', 
2091     padding_bottom: '', 
2092     padding_left: '',
2093     padding_right: '',
2094     padding_x: '',
2095     padding_y: '',
2096     
2097     display: '', 
2098     display_xs: '', 
2099     display_sm: '', 
2100     display_lg: '',
2101     display_xl: '',
2102  
2103     header_image  : '',
2104     header : '',
2105     header_size : 0,
2106     title : '',
2107     subtitle : '',
2108     html : '',
2109     footer: '',
2110
2111     collapsable : false,
2112     collapsed : false,
2113     rotateable : false,
2114     rotated : false,
2115     
2116     dragable : false,
2117     drag_group : false,
2118     dropable : false,
2119     drop_group : false,
2120     childContainer : false,
2121     dropEl : false, /// the dom placeholde element that indicates drop location.
2122     containerEl: false, // body container
2123     bodyEl: false, // card-body
2124     headerContainerEl : false, //
2125     headerEl : false,
2126     header_imageEl : false,
2127     
2128     
2129     layoutCls : function()
2130     {
2131         var cls = '';
2132         var t = this;
2133         Roo.log(this.margin_bottom.length);
2134         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2135             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2136             
2137             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2139             }
2140             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2141                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2142             }
2143         });
2144         
2145         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2146             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2147                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2148             }
2149         });
2150         
2151         // more generic support?
2152         if (this.hidden) {
2153             cls += ' d-none';
2154         }
2155         
2156         return cls;
2157     },
2158  
2159        // Roo.log("Call onRender: " + this.xtype);
2160         /*  We are looking at something like this.
2161 <div class="card">
2162     <img src="..." class="card-img-top" alt="...">
2163     <div class="card-body">
2164         <h5 class="card-title">Card title</h5>
2165          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2166
2167         >> this bit is really the body...
2168         <div> << we will ad dthis in hopefully it will not break shit.
2169         
2170         ** card text does not actually have any styling...
2171         
2172             <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>
2173         
2174         </div> <<
2175           <a href="#" class="card-link">Card link</a>
2176           
2177     </div>
2178     <div class="card-footer">
2179         <small class="text-muted">Last updated 3 mins ago</small>
2180     </div>
2181 </div>
2182          */
2183     getAutoCreate : function(){
2184         
2185         var cfg = {
2186             tag : 'div',
2187             cls : 'card',
2188             cn : [ ]
2189         };
2190         
2191         if (this.weight.length && this.weight != 'light') {
2192             cfg.cls += ' text-white';
2193         } else {
2194             cfg.cls += ' text-dark'; // need as it's nested..
2195         }
2196         if (this.weight.length) {
2197             cfg.cls += ' bg-' + this.weight;
2198         }
2199         
2200         cfg.cls += ' ' + this.layoutCls(); 
2201         
2202         var hdr = false;
2203         var hdr_ctr = false;
2204         if (this.header.length) {
2205             hdr = {
2206                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2207                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208                 cn : []
2209             };
2210             cfg.cn.push(hdr);
2211             hdr_ctr = hdr;
2212         } else {
2213             hdr = {
2214                 tag : 'div',
2215                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2216                 cn : []
2217             };
2218             cfg.cn.push(hdr);
2219             hdr_ctr = hdr;
2220         }
2221         if (this.collapsable) {
2222             hdr_ctr = {
2223             tag : 'a',
2224             cls : 'd-block user-select-none',
2225             cn: [
2226                     {
2227                         tag: 'i',
2228                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2229                     }
2230                    
2231                 ]
2232             };
2233             hdr.cn.push(hdr_ctr);
2234         }
2235         
2236         hdr_ctr.cn.push(        {
2237             tag: 'span',
2238             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2239             html : this.header
2240         });
2241         
2242         
2243         if (this.header_image.length) {
2244             cfg.cn.push({
2245                 tag : 'img',
2246                 cls : 'card-img-top',
2247                 src: this.header_image // escape?
2248             });
2249         } else {
2250             cfg.cn.push({
2251                     tag : 'div',
2252                     cls : 'card-img-top d-none' 
2253                 });
2254         }
2255             
2256         var body = {
2257             tag : 'div',
2258             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2259             cn : []
2260         };
2261         var obody = body;
2262         if (this.collapsable || this.rotateable) {
2263             obody = {
2264                 tag: 'div',
2265                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2266                 cn : [  body ]
2267             };
2268         }
2269         
2270         cfg.cn.push(obody);
2271         
2272         if (this.title.length) {
2273             body.cn.push({
2274                 tag : 'div',
2275                 cls : 'card-title',
2276                 src: this.title // escape?
2277             });
2278         }  
2279         
2280         if (this.subtitle.length) {
2281             body.cn.push({
2282                 tag : 'div',
2283                 cls : 'card-title',
2284                 src: this.subtitle // escape?
2285             });
2286         }
2287         
2288         body.cn.push({
2289             tag : 'div',
2290             cls : 'roo-card-body-ctr'
2291         });
2292         
2293         if (this.html.length) {
2294             body.cn.push({
2295                 tag: 'div',
2296                 html : this.html
2297             });
2298         }
2299         // fixme ? handle objects?
2300         
2301         if (this.footer.length) {
2302            
2303             cfg.cn.push({
2304                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2305                 html : this.footer
2306             });
2307             
2308         } else {
2309             cfg.cn.push({cls : 'card-footer d-none'});
2310         }
2311         
2312         // footer...
2313         
2314         return cfg;
2315     },
2316     
2317     
2318     getCardHeader : function()
2319     {
2320         var  ret = this.el.select('.card-header',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardFooter : function()
2328     {
2329         var  ret = this.el.select('.card-footer',true).first();
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333         
2334         return ret;
2335     },
2336     getCardImageTop : function()
2337     {
2338         var  ret = this.header_imageEl;
2339         if (ret.hasClass('d-none')) {
2340             ret.removeClass('d-none');
2341         }
2342             
2343         return ret;
2344     },
2345     
2346     getChildContainer : function()
2347     {
2348         
2349         if(!this.el){
2350             return false;
2351         }
2352         return this.el.select('.roo-card-body-ctr',true).first();    
2353     },
2354     
2355     initEvents: function() 
2356     {
2357         this.bodyEl = this.el.select('.card-body',true).first(); 
2358         this.containerEl = this.getChildContainer();
2359         if(this.dragable){
2360             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2361                     containerScroll: true,
2362                     ddGroup: this.drag_group || 'default_card_drag_group'
2363             });
2364             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2365         }
2366         if (this.dropable) {
2367             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2368                 containerScroll: true,
2369                 ddGroup: this.drop_group || 'default_card_drag_group'
2370             });
2371             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2372             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2373             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2374             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2375             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2376         }
2377         
2378         if (this.collapsable) {
2379             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2380         }
2381         if (this.rotateable) {
2382             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2383         }
2384         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2385          
2386         this.footerEl = this.el.select('.card-footer',true).first();
2387         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2388         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2389         this.headerEl = this.el.select('.card-header',true).first();
2390         
2391         if (this.rotated) {
2392             this.el.addClass('roo-card-rotated');
2393             this.fireEvent('rotate', this, true);
2394         }
2395         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2396         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2397         
2398     },
2399     getDragData : function(e)
2400     {
2401         var target = this.getEl();
2402         if (target) {
2403             //this.handleSelection(e);
2404             
2405             var dragData = {
2406                 source: this,
2407                 copy: false,
2408                 nodes: this.getEl(),
2409                 records: []
2410             };
2411             
2412             
2413             dragData.ddel = target.dom ;    // the div element
2414             Roo.log(target.getWidth( ));
2415             dragData.ddel.style.width = target.getWidth() + 'px';
2416             
2417             return dragData;
2418         }
2419         return false;
2420     },
2421     /**
2422     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2423     *    whole Element becomes the target, and this causes the drop gesture to append.
2424     *
2425     *    Returns an object:
2426     *     {
2427            
2428            position : 'below' or 'above'
2429            card  : relateive to card OBJECT (or true for no cards listed)
2430            items_n : relative to nth item in list
2431            card_n : relative to  nth card in list
2432     }
2433     *
2434     *    
2435     */
2436     getTargetFromEvent : function(e, dragged_card_el)
2437     {
2438         var target = e.getTarget();
2439         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2440             target = target.parentNode;
2441         }
2442         
2443         var ret = {
2444             position: '',
2445             cards : [],
2446             card_n : -1,
2447             items_n : -1,
2448             card : false 
2449         };
2450         
2451         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2452         // see if target is one of the 'cards'...
2453         
2454         
2455         //Roo.log(this.items.length);
2456         var pos = false;
2457         
2458         var last_card_n = 0;
2459         var cards_len  = 0;
2460         for (var i = 0;i< this.items.length;i++) {
2461             
2462             if (!this.items[i].el.hasClass('card')) {
2463                  continue;
2464             }
2465             pos = this.getDropPoint(e, this.items[i].el.dom);
2466             
2467             cards_len = ret.cards.length;
2468             //Roo.log(this.items[i].el.dom.id);
2469             ret.cards.push(this.items[i]);
2470             last_card_n  = i;
2471             if (ret.card_n < 0 && pos == 'above') {
2472                 ret.position = cards_len > 0 ? 'below' : pos;
2473                 ret.items_n = i > 0 ? i - 1 : 0;
2474                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2475                 ret.card = ret.cards[ret.card_n];
2476             }
2477         }
2478         if (!ret.cards.length) {
2479             ret.card = true;
2480             ret.position = 'below';
2481             ret.items_n;
2482             return ret;
2483         }
2484         // could not find a card.. stick it at the end..
2485         if (ret.card_n < 0) {
2486             ret.card_n = last_card_n;
2487             ret.card = ret.cards[last_card_n];
2488             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2489             ret.position = 'below';
2490         }
2491         
2492         if (this.items[ret.items_n].el == dragged_card_el) {
2493             return false;
2494         }
2495         
2496         if (ret.position == 'below') {
2497             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2498             
2499             if (card_after  && card_after.el == dragged_card_el) {
2500                 return false;
2501             }
2502             return ret;
2503         }
2504         
2505         // its's after ..
2506         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2507         
2508         if (card_before  && card_before.el == dragged_card_el) {
2509             return false;
2510         }
2511         
2512         return ret;
2513     },
2514     
2515     onNodeEnter : function(n, dd, e, data){
2516         return false;
2517     },
2518     onNodeOver : function(n, dd, e, data)
2519     {
2520        
2521         var target_info = this.getTargetFromEvent(e,data.source.el);
2522         if (target_info === false) {
2523             this.dropPlaceHolder('hide');
2524             return false;
2525         }
2526         Roo.log(['getTargetFromEvent', target_info ]);
2527         
2528         
2529         if (this.fireEvent('cardover', this, [ data ]) === false) {
2530             return false;
2531         }
2532         
2533         this.dropPlaceHolder('show', target_info,data);
2534         
2535         return false; 
2536     },
2537     onNodeOut : function(n, dd, e, data){
2538         this.dropPlaceHolder('hide');
2539      
2540     },
2541     onNodeDrop : function(n, dd, e, data)
2542     {
2543         
2544         // call drop - return false if
2545         
2546         // this could actually fail - if the Network drops..
2547         // we will ignore this at present..- client should probably reload
2548         // the whole set of cards if stuff like that fails.
2549         
2550         
2551         var info = this.getTargetFromEvent(e,data.source.el);
2552         if (info === false) {
2553             return false;
2554         }
2555         this.dropPlaceHolder('hide');
2556   
2557           
2558     
2559         this.acceptCard(data.source, info.position, info.card, info.items_n);
2560         return true;
2561          
2562     },
2563     firstChildCard : function()
2564     {
2565         for (var i = 0;i< this.items.length;i++) {
2566             
2567             if (!this.items[i].el.hasClass('card')) {
2568                  continue;
2569             }
2570             return this.items[i];
2571         }
2572         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2573     },
2574     /**
2575      * accept card
2576      *
2577      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2578      */
2579     acceptCard : function(move_card,  position, next_to_card )
2580     {
2581         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2582             return false;
2583         }
2584         
2585         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2586         
2587         move_card.parent().removeCard(move_card);
2588         
2589         
2590         var dom = move_card.el.dom;
2591         dom.style.width = ''; // clear with - which is set by drag.
2592         
2593         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2594             var cardel = next_to_card.el.dom;
2595             
2596             if (position == 'above' ) {
2597                 cardel.parentNode.insertBefore(dom, cardel);
2598             } else if (cardel.nextSibling) {
2599                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2600             } else {
2601                 cardel.parentNode.append(dom);
2602             }
2603         } else {
2604             // card container???
2605             this.containerEl.dom.append(dom);
2606         }
2607         
2608         //FIXME HANDLE card = true 
2609         
2610         // add this to the correct place in items.
2611         
2612         // remove Card from items.
2613         
2614        
2615         if (this.items.length) {
2616             var nitems = [];
2617             //Roo.log([info.items_n, info.position, this.items.length]);
2618             for (var i =0; i < this.items.length; i++) {
2619                 if (i == to_items_n && position == 'above') {
2620                     nitems.push(move_card);
2621                 }
2622                 nitems.push(this.items[i]);
2623                 if (i == to_items_n && position == 'below') {
2624                     nitems.push(move_card);
2625                 }
2626             }
2627             this.items = nitems;
2628             Roo.log(this.items);
2629         } else {
2630             this.items.push(move_card);
2631         }
2632         
2633         move_card.parentId = this.id;
2634         
2635         return true;
2636         
2637         
2638     },
2639     removeCard : function(c)
2640     {
2641         this.items = this.items.filter(function(e) { return e != c });
2642  
2643         var dom = c.el.dom;
2644         dom.parentNode.removeChild(dom);
2645         dom.style.width = ''; // clear with - which is set by drag.
2646         c.parentId = false;
2647         
2648     },
2649     
2650     /**    Decide whether to drop above or below a View node. */
2651     getDropPoint : function(e, n, dd)
2652     {
2653         if (dd) {
2654              return false;
2655         }
2656         if (n == this.containerEl.dom) {
2657             return "above";
2658         }
2659         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2660         var c = t + (b - t) / 2;
2661         var y = Roo.lib.Event.getPageY(e);
2662         if(y <= c) {
2663             return "above";
2664         }else{
2665             return "below";
2666         }
2667     },
2668     onToggleCollapse : function(e)
2669         {
2670         if (this.collapsed) {
2671             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2672             this.collapsableEl.addClass('show');
2673             this.collapsed = false;
2674             return;
2675         }
2676         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2677         this.collapsableEl.removeClass('show');
2678         this.collapsed = true;
2679         
2680     
2681     },
2682     
2683     onToggleRotate : function(e)
2684     {
2685         this.collapsableEl.removeClass('show');
2686         this.footerEl.removeClass('d-none');
2687         this.el.removeClass('roo-card-rotated');
2688         this.el.removeClass('d-none');
2689         if (this.rotated) {
2690             
2691             this.collapsableEl.addClass('show');
2692             this.rotated = false;
2693             this.fireEvent('rotate', this, this.rotated);
2694             return;
2695         }
2696         this.el.addClass('roo-card-rotated');
2697         this.footerEl.addClass('d-none');
2698         this.el.select('.roo-collapsable').removeClass('show');
2699         
2700         this.rotated = true;
2701         this.fireEvent('rotate', this, this.rotated);
2702     
2703     },
2704     
2705     dropPlaceHolder: function (action, info, data)
2706     {
2707         if (this.dropEl === false) {
2708             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2709             cls : 'd-none'
2710             },true);
2711         }
2712         this.dropEl.removeClass(['d-none', 'd-block']);        
2713         if (action == 'hide') {
2714             
2715             this.dropEl.addClass('d-none');
2716             return;
2717         }
2718         // FIXME - info.card == true!!!
2719         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2720         
2721         if (info.card !== true) {
2722             var cardel = info.card.el.dom;
2723             
2724             if (info.position == 'above') {
2725                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2726             } else if (cardel.nextSibling) {
2727                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2728             } else {
2729                 cardel.parentNode.append(this.dropEl.dom);
2730             }
2731         } else {
2732             // card container???
2733             this.containerEl.dom.append(this.dropEl.dom);
2734         }
2735         
2736         this.dropEl.addClass('d-block roo-card-dropzone');
2737         
2738         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2739         
2740         
2741     
2742     
2743     
2744     },
2745     setHeaderText: function(html)
2746     {
2747         this.header = html;
2748         if (this.headerContainerEl) {
2749             this.headerContainerEl.dom.innerHTML = html;
2750         }
2751     },
2752     onHeaderImageLoad : function(ev, he)
2753     {
2754         if (!this.header_image_fit_square) {
2755             return;
2756         }
2757         
2758         var hw = he.naturalHeight / he.naturalWidth;
2759         // wide image = < 0
2760         // tall image = > 1
2761         //var w = he.dom.naturalWidth;
2762         var ww = he.width;
2763         he.style.left =  0;
2764         he.style.position =  'relative';
2765         if (hw > 1) {
2766             var nw = (ww * (1/hw));
2767             Roo.get(he).setSize( ww * (1/hw),  ww);
2768             he.style.left =  ((ww - nw)/ 2) + 'px';
2769             he.style.position =  'relative';
2770         }
2771
2772     }
2773
2774     
2775 });
2776
2777 /*
2778  * - LGPL
2779  *
2780  * Card header - holder for the card header elements.
2781  * 
2782  */
2783
2784 /**
2785  * @class Roo.bootstrap.CardHeader
2786  * @extends Roo.bootstrap.Element
2787  * @parent Roo.bootstrap.Card
2788  * @children Roo.bootstrap.Component
2789  * Bootstrap CardHeader class
2790  * @constructor
2791  * Create a new Card Header - that you can embed children into
2792  * @param {Object} config The config object
2793  */
2794
2795 Roo.bootstrap.CardHeader = function(config){
2796     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2797 };
2798
2799 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2800     
2801     
2802     container_method : 'getCardHeader' 
2803     
2804      
2805     
2806     
2807    
2808 });
2809
2810  
2811
2812  /*
2813  * - LGPL
2814  *
2815  * Card footer - holder for the card footer elements.
2816  * 
2817  */
2818
2819 /**
2820  * @class Roo.bootstrap.CardFooter
2821  * @extends Roo.bootstrap.Element
2822  * @parent Roo.bootstrap.Card
2823  * @children Roo.bootstrap.Component
2824  * Bootstrap CardFooter class
2825  * 
2826  * @constructor
2827  * Create a new Card Footer - that you can embed children into
2828  * @param {Object} config The config object
2829  */
2830
2831 Roo.bootstrap.CardFooter = function(config){
2832     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2833 };
2834
2835 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2836     
2837     
2838     container_method : 'getCardFooter' 
2839     
2840      
2841     
2842     
2843    
2844 });
2845
2846  
2847
2848  /*
2849  * - LGPL
2850  *
2851  * Card header - holder for the card header elements.
2852  * 
2853  */
2854
2855 /**
2856  * @class Roo.bootstrap.CardImageTop
2857  * @extends Roo.bootstrap.Element
2858  * @parent Roo.bootstrap.Card
2859  * @children Roo.bootstrap.Component
2860  * Bootstrap CardImageTop class
2861  * 
2862  * @constructor
2863  * Create a new Card Image Top container
2864  * @param {Object} config The config object
2865  */
2866
2867 Roo.bootstrap.CardImageTop = function(config){
2868     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2869 };
2870
2871 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2872     
2873    
2874     container_method : 'getCardImageTop' 
2875     
2876      
2877     
2878    
2879 });
2880
2881  
2882
2883  
2884 /*
2885 * Licence: LGPL
2886 */
2887
2888 /**
2889  * @class Roo.bootstrap.ButtonUploader
2890  * @extends Roo.bootstrap.Button
2891  * Bootstrap Button Uploader class - it's a button which when you add files to it
2892  *
2893  * 
2894  * @cfg {Number} errorTimeout default 3000
2895  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2896  * @cfg {Array}  html The button text.
2897  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2898  *
2899  * @constructor
2900  * Create a new CardUploader
2901  * @param {Object} config The config object
2902  */
2903
2904 Roo.bootstrap.ButtonUploader = function(config){
2905     
2906  
2907     
2908     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2909     
2910      
2911      this.addEvents({
2912          // raw events
2913         /**
2914          * @event beforeselect
2915          * When button is pressed, before show upload files dialog is shown
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          *
2918          */
2919         'beforeselect' : true,
2920          /**
2921          * @event fired when files have been selected, 
2922          * When a the download link is clicked
2923          * @param {Roo.bootstrap.UploaderButton} this
2924          * @param {Array} Array of files that have been uploaded
2925          */
2926         'uploaded' : true
2927         
2928     });
2929 };
2930  
2931 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2932     
2933      
2934     errorTimeout : 3000,
2935      
2936     images : false,
2937    
2938     fileCollection : false,
2939     allowBlank : true,
2940     
2941     multiple : true,
2942     
2943     getAutoCreate : function()
2944     {
2945        
2946         
2947         return  {
2948             cls :'div' ,
2949             cn : [
2950                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2951             ]
2952         };
2953            
2954          
2955     },
2956      
2957    
2958     initEvents : function()
2959     {
2960         
2961         Roo.bootstrap.Button.prototype.initEvents.call(this);
2962         
2963         
2964         
2965         
2966         
2967         this.urlAPI = (window.createObjectURL && window) || 
2968                                 (window.URL && URL.revokeObjectURL && URL) || 
2969                                 (window.webkitURL && webkitURL);
2970                         
2971         var im = {
2972             tag: 'input',
2973             type : 'file',
2974             cls : 'd-none  roo-card-upload-selector' 
2975           
2976         };
2977         if (this.multiple) {
2978             im.multiple = 'multiple';
2979         }
2980         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2981        
2982         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2983         
2984         this.selectorEl.on('change', this.onFileSelected, this);
2985          
2986          
2987        
2988     },
2989     
2990    
2991     onClick : function(e)
2992     {
2993         e.preventDefault();
2994         
2995         if ( this.fireEvent('beforeselect', this) === false) {
2996             return;
2997         }
2998          
2999         this.selectorEl.dom.click();
3000          
3001     },
3002     
3003     onFileSelected : function(e)
3004     {
3005         e.preventDefault();
3006         
3007         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3008             return;
3009         }
3010         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3011         this.selectorEl.dom.value  = '';// hopefully reset..
3012         
3013         this.fireEvent('uploaded', this,  files );
3014         
3015     },
3016     
3017        
3018    
3019     
3020     /**
3021      * addCard - add an Attachment to the uploader
3022      * @param data - the data about the image to upload
3023      *
3024      * {
3025           id : 123
3026           title : "Title of file",
3027           is_uploaded : false,
3028           src : "http://.....",
3029           srcfile : { the File upload object },
3030           mimetype : file.type,
3031           preview : false,
3032           is_deleted : 0
3033           .. any other data...
3034         }
3035      *
3036      * 
3037     */
3038      
3039     reset: function()
3040     {
3041          
3042          this.selectorEl
3043     } 
3044     
3045     
3046     
3047     
3048 });
3049  /*
3050  * - LGPL
3051  *
3052  * image
3053  * 
3054  */
3055
3056
3057 /**
3058  * @class Roo.bootstrap.Img
3059  * @extends Roo.bootstrap.Component
3060  * Bootstrap Img class
3061  * @cfg {Boolean} imgResponsive false | true
3062  * @cfg {String} border rounded | circle | thumbnail
3063  * @cfg {String} src image source
3064  * @cfg {String} alt image alternative text
3065  * @cfg {String} href a tag href
3066  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3067  * @cfg {String} xsUrl xs image source
3068  * @cfg {String} smUrl sm image source
3069  * @cfg {String} mdUrl md image source
3070  * @cfg {String} lgUrl lg image source
3071  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3072  * 
3073  * @constructor
3074  * Create a new Input
3075  * @param {Object} config The config object
3076  */
3077
3078 Roo.bootstrap.Img = function(config){
3079     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3080     
3081     this.addEvents({
3082         // img events
3083         /**
3084          * @event click
3085          * The img click event for the img.
3086          * @param {Roo.EventObject} e
3087          */
3088         "click" : true,
3089         /**
3090          * @event load
3091          * The when any image loads
3092          * @param {Roo.EventObject} e
3093          */
3094         "load" : true
3095     });
3096 };
3097
3098 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3099     
3100     imgResponsive: true,
3101     border: '',
3102     src: 'about:blank',
3103     href: false,
3104     target: false,
3105     xsUrl: '',
3106     smUrl: '',
3107     mdUrl: '',
3108     lgUrl: '',
3109     backgroundContain : false,
3110
3111     getAutoCreate : function()
3112     {   
3113         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3114             return this.createSingleImg();
3115         }
3116         
3117         var cfg = {
3118             tag: 'div',
3119             cls: 'roo-image-responsive-group',
3120             cn: []
3121         };
3122         var _this = this;
3123         
3124         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3125             
3126             if(!_this[size + 'Url']){
3127                 return;
3128             }
3129             
3130             var img = {
3131                 tag: 'img',
3132                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3133                 html: _this.html || cfg.html,
3134                 src: _this[size + 'Url']
3135             };
3136             
3137             img.cls += ' roo-image-responsive-' + size;
3138             
3139             var s = ['xs', 'sm', 'md', 'lg'];
3140             
3141             s.splice(s.indexOf(size), 1);
3142             
3143             Roo.each(s, function(ss){
3144                 img.cls += ' hidden-' + ss;
3145             });
3146             
3147             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3148                 cfg.cls += ' img-' + _this.border;
3149             }
3150             
3151             if(_this.alt){
3152                 cfg.alt = _this.alt;
3153             }
3154             
3155             if(_this.href){
3156                 var a = {
3157                     tag: 'a',
3158                     href: _this.href,
3159                     cn: [
3160                         img
3161                     ]
3162                 };
3163
3164                 if(this.target){
3165                     a.target = _this.target;
3166                 }
3167             }
3168             
3169             cfg.cn.push((_this.href) ? a : img);
3170             
3171         });
3172         
3173         return cfg;
3174     },
3175     
3176     createSingleImg : function()
3177     {
3178         var cfg = {
3179             tag: 'img',
3180             cls: (this.imgResponsive) ? 'img-responsive' : '',
3181             html : null,
3182             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3183         };
3184         
3185         if (this.backgroundContain) {
3186             cfg.cls += ' background-contain';
3187         }
3188         
3189         cfg.html = this.html || cfg.html;
3190         
3191         if (this.backgroundContain) {
3192             cfg.style="background-image: url(" + this.src + ')';
3193         } else {
3194             cfg.src = this.src || cfg.src;
3195         }
3196         
3197         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3198             cfg.cls += ' img-' + this.border;
3199         }
3200         
3201         if(this.alt){
3202             cfg.alt = this.alt;
3203         }
3204         
3205         if(this.href){
3206             var a = {
3207                 tag: 'a',
3208                 href: this.href,
3209                 cn: [
3210                     cfg
3211                 ]
3212             };
3213             
3214             if(this.target){
3215                 a.target = this.target;
3216             }
3217             
3218         }
3219         
3220         return (this.href) ? a : cfg;
3221     },
3222     
3223     initEvents: function() 
3224     {
3225         if(!this.href){
3226             this.el.on('click', this.onClick, this);
3227         }
3228         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3229             this.el.on('load', this.onImageLoad, this);
3230         } else {
3231             // not sure if this works.. not tested
3232             this.el.select('img', true).on('load', this.onImageLoad, this);
3233         }
3234         
3235     },
3236     
3237     onClick : function(e)
3238     {
3239         Roo.log('img onclick');
3240         this.fireEvent('click', this, e);
3241     },
3242     onImageLoad: function(e)
3243     {
3244         Roo.log('img load');
3245         this.fireEvent('load', this, e);
3246     },
3247     
3248     /**
3249      * Sets the url of the image - used to update it
3250      * @param {String} url the url of the image
3251      */
3252     
3253     setSrc : function(url)
3254     {
3255         this.src =  url;
3256         
3257         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3258             if (this.backgroundContain) {
3259                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3260             } else {
3261                 this.el.dom.src =  url;
3262             }
3263             return;
3264         }
3265         
3266         this.el.select('img', true).first().dom.src =  url;
3267     }
3268     
3269     
3270    
3271 });
3272
3273  /*
3274  * - LGPL
3275  *
3276  * image
3277  * 
3278  */
3279
3280
3281 /**
3282  * @class Roo.bootstrap.Link
3283  * @extends Roo.bootstrap.Component
3284  * @children Roo.bootstrap.Component
3285  * Bootstrap Link Class (eg. '<a href>')
3286  
3287  * @cfg {String} alt image alternative text
3288  * @cfg {String} href a tag href
3289  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3290  * @cfg {String} html the content of the link.
3291  * @cfg {String} anchor name for the anchor link
3292  * @cfg {String} fa - favicon
3293
3294  * @cfg {Boolean} preventDefault (true | false) default false
3295
3296  * 
3297  * @constructor
3298  * Create a new Input
3299  * @param {Object} config The config object
3300  */
3301
3302 Roo.bootstrap.Link = function(config){
3303     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3304     
3305     this.addEvents({
3306         // img events
3307         /**
3308          * @event click
3309          * The img click event for the img.
3310          * @param {Roo.EventObject} e
3311          */
3312         "click" : true
3313     });
3314 };
3315
3316 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3317     
3318     href: false,
3319     target: false,
3320     preventDefault: false,
3321     anchor : false,
3322     alt : false,
3323     fa: false,
3324
3325
3326     getAutoCreate : function()
3327     {
3328         var html = this.html || '';
3329         
3330         if (this.fa !== false) {
3331             html = '<i class="fa fa-' + this.fa + '"></i>';
3332         }
3333         var cfg = {
3334             tag: 'a'
3335         };
3336         // anchor's do not require html/href...
3337         if (this.anchor === false) {
3338             cfg.html = html;
3339             cfg.href = this.href || '#';
3340         } else {
3341             cfg.name = this.anchor;
3342             if (this.html !== false || this.fa !== false) {
3343                 cfg.html = html;
3344             }
3345             if (this.href !== false) {
3346                 cfg.href = this.href;
3347             }
3348         }
3349         
3350         if(this.alt !== false){
3351             cfg.alt = this.alt;
3352         }
3353         
3354         
3355         if(this.target !== false) {
3356             cfg.target = this.target;
3357         }
3358         
3359         return cfg;
3360     },
3361     
3362     initEvents: function() {
3363         
3364         if(!this.href || this.preventDefault){
3365             this.el.on('click', this.onClick, this);
3366         }
3367     },
3368     
3369     onClick : function(e)
3370     {
3371         if(this.preventDefault){
3372             e.preventDefault();
3373         }
3374         //Roo.log('img onclick');
3375         this.fireEvent('click', this, e);
3376     }
3377    
3378 });
3379
3380  /*
3381  * - LGPL
3382  *
3383  * header
3384  * 
3385  */
3386
3387 /**
3388  * @class Roo.bootstrap.Header
3389  * @extends Roo.bootstrap.Component
3390  * @children Roo.bootstrap.Component
3391  * Bootstrap Header class
3392  *
3393  * 
3394  * @cfg {String} html content of header
3395  * @cfg {Number} level (1|2|3|4|5|6) default 1
3396  * 
3397  * @constructor
3398  * Create a new Header
3399  * @param {Object} config The config object
3400  */
3401
3402
3403 Roo.bootstrap.Header  = function(config){
3404     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3405 };
3406
3407 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3408     
3409     //href : false,
3410     html : false,
3411     level : 1,
3412     
3413     
3414     
3415     getAutoCreate : function(){
3416         
3417         
3418         
3419         var cfg = {
3420             tag: 'h' + (1 *this.level),
3421             html: this.html || ''
3422         } ;
3423         
3424         return cfg;
3425     }
3426    
3427 });
3428
3429  
3430
3431  /**
3432  * @class Roo.bootstrap.MenuMgr
3433  * @licence LGPL
3434  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3435  * @static
3436  */
3437 Roo.bootstrap.menu.Manager = function(){
3438    var menus, active, groups = {}, attached = false, lastShow = new Date();
3439
3440    // private - called when first menu is created
3441    function init(){
3442        menus = {};
3443        active = new Roo.util.MixedCollection();
3444        Roo.get(document).addKeyListener(27, function(){
3445            if(active.length > 0){
3446                hideAll();
3447            }
3448        });
3449    }
3450
3451    // private
3452    function hideAll(){
3453        if(active && active.length > 0){
3454            var c = active.clone();
3455            c.each(function(m){
3456                m.hide();
3457            });
3458        }
3459    }
3460
3461    // private
3462    function onHide(m){
3463        active.remove(m);
3464        if(active.length < 1){
3465            Roo.get(document).un("mouseup", onMouseDown);
3466             
3467            attached = false;
3468        }
3469    }
3470
3471    // private
3472    function onShow(m){
3473        var last = active.last();
3474        lastShow = new Date();
3475        active.add(m);
3476        if(!attached){
3477           Roo.get(document).on("mouseup", onMouseDown);
3478            
3479            attached = true;
3480        }
3481        if(m.parentMenu){
3482           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3483           m.parentMenu.activeChild = m;
3484        }else if(last && last.isVisible()){
3485           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3486        }
3487    }
3488
3489    // private
3490    function onBeforeHide(m){
3491        if(m.activeChild){
3492            m.activeChild.hide();
3493        }
3494        if(m.autoHideTimer){
3495            clearTimeout(m.autoHideTimer);
3496            delete m.autoHideTimer;
3497        }
3498    }
3499
3500    // private
3501    function onBeforeShow(m){
3502        var pm = m.parentMenu;
3503        if(!pm && !m.allowOtherMenus){
3504            hideAll();
3505        }else if(pm && pm.activeChild && active != m){
3506            pm.activeChild.hide();
3507        }
3508    }
3509
3510    // private this should really trigger on mouseup..
3511    function onMouseDown(e){
3512         Roo.log("on Mouse Up");
3513         
3514         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3515             Roo.log("MenuManager hideAll");
3516             hideAll();
3517             e.stopEvent();
3518         }
3519         
3520         
3521    }
3522
3523    // private
3524    function onBeforeCheck(mi, state){
3525        if(state){
3526            var g = groups[mi.group];
3527            for(var i = 0, l = g.length; i < l; i++){
3528                if(g[i] != mi){
3529                    g[i].setChecked(false);
3530                }
3531            }
3532        }
3533    }
3534
3535    return {
3536
3537        /**
3538         * Hides all menus that are currently visible
3539         */
3540        hideAll : function(){
3541             hideAll();  
3542        },
3543
3544        // private
3545        register : function(menu){
3546            if(!menus){
3547                init();
3548            }
3549            menus[menu.id] = menu;
3550            menu.on("beforehide", onBeforeHide);
3551            menu.on("hide", onHide);
3552            menu.on("beforeshow", onBeforeShow);
3553            menu.on("show", onShow);
3554            var g = menu.group;
3555            if(g && menu.events["checkchange"]){
3556                if(!groups[g]){
3557                    groups[g] = [];
3558                }
3559                groups[g].push(menu);
3560                menu.on("checkchange", onCheck);
3561            }
3562        },
3563
3564         /**
3565          * Returns a {@link Roo.menu.Menu} object
3566          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3567          * be used to generate and return a new Menu instance.
3568          */
3569        get : function(menu){
3570            if(typeof menu == "string"){ // menu id
3571                return menus[menu];
3572            }else if(menu.events){  // menu instance
3573                return menu;
3574            }
3575            /*else if(typeof menu.length == 'number'){ // array of menu items?
3576                return new Roo.bootstrap.Menu({items:menu});
3577            }else{ // otherwise, must be a config
3578                return new Roo.bootstrap.Menu(menu);
3579            }
3580            */
3581            return false;
3582        },
3583
3584        // private
3585        unregister : function(menu){
3586            delete menus[menu.id];
3587            menu.un("beforehide", onBeforeHide);
3588            menu.un("hide", onHide);
3589            menu.un("beforeshow", onBeforeShow);
3590            menu.un("show", onShow);
3591            var g = menu.group;
3592            if(g && menu.events["checkchange"]){
3593                groups[g].remove(menu);
3594                menu.un("checkchange", onCheck);
3595            }
3596        },
3597
3598        // private
3599        registerCheckable : function(menuItem){
3600            var g = menuItem.group;
3601            if(g){
3602                if(!groups[g]){
3603                    groups[g] = [];
3604                }
3605                groups[g].push(menuItem);
3606                menuItem.on("beforecheckchange", onBeforeCheck);
3607            }
3608        },
3609
3610        // private
3611        unregisterCheckable : function(menuItem){
3612            var g = menuItem.group;
3613            if(g){
3614                groups[g].remove(menuItem);
3615                menuItem.un("beforecheckchange", onBeforeCheck);
3616            }
3617        }
3618    };
3619 }(); 
3620 /**
3621  * @class Roo.bootstrap.menu.Menu
3622  * @extends Roo.bootstrap.Component
3623  * @licence LGPL
3624  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3625  * @parent none
3626  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3627  * 
3628  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3629  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3630  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3631  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3632 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3633 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3634  
3635  * @constructor
3636  * Create a new Menu
3637  * @param {Object} config The config objectQ
3638  */
3639
3640
3641 Roo.bootstrap.menu.Menu = function(config){
3642     
3643     if (config.type == 'treeview') {
3644         // normally menu's are drawn attached to the document to handle layering etc..
3645         // however treeview (used by the docs menu is drawn into the parent element)
3646         this.container_method = 'getChildContainer'; 
3647     }
3648     
3649     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3650     if (this.registerMenu && this.type != 'treeview')  {
3651         Roo.bootstrap.menu.Manager.register(this);
3652     }
3653     
3654     
3655     this.addEvents({
3656         /**
3657          * @event beforeshow
3658          * Fires before this menu is displayed (return false to block)
3659          * @param {Roo.menu.Menu} this
3660          */
3661         beforeshow : true,
3662         /**
3663          * @event beforehide
3664          * Fires before this menu is hidden (return false to block)
3665          * @param {Roo.menu.Menu} this
3666          */
3667         beforehide : true,
3668         /**
3669          * @event show
3670          * Fires after this menu is displayed
3671          * @param {Roo.menu.Menu} this
3672          */
3673         show : true,
3674         /**
3675          * @event hide
3676          * Fires after this menu is hidden
3677          * @param {Roo.menu.Menu} this
3678          */
3679         hide : true,
3680         /**
3681          * @event click
3682          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3683          * @param {Roo.menu.Menu} this
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          * @param {Roo.EventObject} e
3686          */
3687         click : true,
3688         /**
3689          * @event mouseover
3690          * Fires when the mouse is hovering over this menu
3691          * @param {Roo.menu.Menu} this
3692          * @param {Roo.EventObject} e
3693          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694          */
3695         mouseover : true,
3696         /**
3697          * @event mouseout
3698          * Fires when the mouse exits this menu
3699          * @param {Roo.menu.Menu} this
3700          * @param {Roo.EventObject} e
3701          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3702          */
3703         mouseout : true,
3704         /**
3705          * @event itemclick
3706          * Fires when a menu item contained in this menu is clicked
3707          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3708          * @param {Roo.EventObject} e
3709          */
3710         itemclick: true
3711     });
3712     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3713 };
3714
3715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3716     
3717    /// html : false,
3718    
3719     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3720     type: false,
3721     /**
3722      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3723      */
3724     registerMenu : true,
3725     
3726     menuItems :false, // stores the menu items..
3727     
3728     hidden:true,
3729         
3730     parentMenu : false,
3731     
3732     stopEvent : true,
3733     
3734     isLink : false,
3735     
3736     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3737     
3738     hideTrigger : false,
3739     
3740     align : 'tl-bl?',
3741     
3742     
3743     getChildContainer : function() {
3744         return this.el;  
3745     },
3746     
3747     getAutoCreate : function(){
3748          
3749         //if (['right'].indexOf(this.align)!==-1) {
3750         //    cfg.cn[1].cls += ' pull-right'
3751         //}
3752          
3753         var cfg = {
3754             tag : 'ul',
3755             cls : 'dropdown-menu shadow' ,
3756             style : 'z-index:1000'
3757             
3758         };
3759         
3760         if (this.type === 'submenu') {
3761             cfg.cls = 'submenu active';
3762         }
3763         if (this.type === 'treeview') {
3764             cfg.cls = 'treeview-menu';
3765         }
3766         
3767         return cfg;
3768     },
3769     initEvents : function() {
3770         
3771        // Roo.log("ADD event");
3772        // Roo.log(this.triggerEl.dom);
3773         if (this.triggerEl) {
3774             
3775             this.triggerEl.on('click', this.onTriggerClick, this);
3776             
3777             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3778             
3779             if (!this.hideTrigger) {
3780                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3781                     // dropdown toggle on the 'a' in BS4?
3782                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3783                 } else {
3784                     this.triggerEl.addClass('dropdown-toggle');
3785                 }
3786             }
3787         }
3788         
3789         if (Roo.isTouch) {
3790             this.el.on('touchstart'  , this.onTouch, this);
3791         }
3792         this.el.on('click' , this.onClick, this);
3793
3794         this.el.on("mouseover", this.onMouseOver, this);
3795         this.el.on("mouseout", this.onMouseOut, this);
3796         
3797     },
3798     
3799     findTargetItem : function(e)
3800     {
3801         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3802         if(!t){
3803             return false;
3804         }
3805         //Roo.log(t);         Roo.log(t.id);
3806         if(t && t.id){
3807             //Roo.log(this.menuitems);
3808             return this.menuitems.get(t.id);
3809             
3810             //return this.items.get(t.menuItemId);
3811         }
3812         
3813         return false;
3814     },
3815     
3816     onTouch : function(e) 
3817     {
3818         Roo.log("menu.onTouch");
3819         //e.stopEvent(); this make the user popdown broken
3820         this.onClick(e);
3821     },
3822     
3823     onClick : function(e)
3824     {
3825         Roo.log("menu.onClick");
3826         
3827         var t = this.findTargetItem(e);
3828         if(!t || t.isContainer){
3829             return;
3830         }
3831         Roo.log(e);
3832         /*
3833         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3834             if(t == this.activeItem && t.shouldDeactivate(e)){
3835                 this.activeItem.deactivate();
3836                 delete this.activeItem;
3837                 return;
3838             }
3839             if(t.canActivate){
3840                 this.setActiveItem(t, true);
3841             }
3842             return;
3843             
3844             
3845         }
3846         */
3847        
3848         Roo.log('pass click event');
3849         
3850         t.onClick(e);
3851         
3852         this.fireEvent("click", this, t, e);
3853         
3854         var _this = this;
3855         
3856         if(!t.href.length || t.href == '#'){
3857             (function() { _this.hide(); }).defer(100);
3858         }
3859         
3860     },
3861     
3862     onMouseOver : function(e){
3863         var t  = this.findTargetItem(e);
3864         //Roo.log(t);
3865         //if(t){
3866         //    if(t.canActivate && !t.disabled){
3867         //        this.setActiveItem(t, true);
3868         //    }
3869         //}
3870         
3871         this.fireEvent("mouseover", this, e, t);
3872     },
3873     isVisible : function(){
3874         return !this.hidden;
3875     },
3876     onMouseOut : function(e){
3877         var t  = this.findTargetItem(e);
3878         
3879         //if(t ){
3880         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3881         //        this.activeItem.deactivate();
3882         //        delete this.activeItem;
3883         //    }
3884         //}
3885         this.fireEvent("mouseout", this, e, t);
3886     },
3887     
3888     
3889     /**
3890      * Displays this menu relative to another element
3891      * @param {String/HTMLElement/Roo.Element} element The element to align to
3892      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3893      * the element (defaults to this.defaultAlign)
3894      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3895      */
3896     show : function(el, pos, parentMenu)
3897     {
3898         if (false === this.fireEvent("beforeshow", this)) {
3899             Roo.log("show canceled");
3900             return;
3901         }
3902         this.parentMenu = parentMenu;
3903         if(!this.el){
3904             this.render();
3905         }
3906         this.el.addClass('show'); // show otherwise we do not know how big we are..
3907          
3908         var xy = this.el.getAlignToXY(el, pos);
3909         
3910         // bl-tl << left align  below
3911         // tl-bl << left align 
3912         
3913         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3914             // if it goes to far to the right.. -> align left.
3915             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3916         }
3917         if(xy[0] < 0){
3918             // was left align - go right?
3919             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3920         }
3921         
3922         // goes down the bottom
3923         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3924            xy[1]  < 0 ){
3925             var a = this.align.replace('?', '').split('-');
3926             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3927             
3928         }
3929         
3930         this.showAt(  xy , parentMenu, false);
3931     },
3932      /**
3933      * Displays this menu at a specific xy position
3934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3936      */
3937     showAt : function(xy, parentMenu, /* private: */_e){
3938         this.parentMenu = parentMenu;
3939         if(!this.el){
3940             this.render();
3941         }
3942         if(_e !== false){
3943             this.fireEvent("beforeshow", this);
3944             //xy = this.el.adjustForConstraints(xy);
3945         }
3946         
3947         //this.el.show();
3948         this.hideMenuItems();
3949         this.hidden = false;
3950         if (this.triggerEl) {
3951             this.triggerEl.addClass('open');
3952         }
3953         
3954         this.el.addClass('show');
3955         
3956         
3957         
3958         // reassign x when hitting right
3959         
3960         // reassign y when hitting bottom
3961         
3962         // but the list may align on trigger left or trigger top... should it be a properity?
3963         
3964         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3965             this.el.setXY(xy);
3966         }
3967         
3968         this.focus();
3969         this.fireEvent("show", this);
3970     },
3971     
3972     focus : function(){
3973         return;
3974         if(!this.hidden){
3975             this.doFocus.defer(50, this);
3976         }
3977     },
3978
3979     doFocus : function(){
3980         if(!this.hidden){
3981             this.focusEl.focus();
3982         }
3983     },
3984
3985     /**
3986      * Hides this menu and optionally all parent menus
3987      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3988      */
3989     hide : function(deep)
3990     {
3991         if (false === this.fireEvent("beforehide", this)) {
3992             Roo.log("hide canceled");
3993             return;
3994         }
3995         this.hideMenuItems();
3996         if(this.el && this.isVisible()){
3997            
3998             if(this.activeItem){
3999                 this.activeItem.deactivate();
4000                 this.activeItem = null;
4001             }
4002             if (this.triggerEl) {
4003                 this.triggerEl.removeClass('open');
4004             }
4005             
4006             this.el.removeClass('show');
4007             this.hidden = true;
4008             this.fireEvent("hide", this);
4009         }
4010         if(deep === true && this.parentMenu){
4011             this.parentMenu.hide(true);
4012         }
4013     },
4014     
4015     onTriggerClick : function(e)
4016     {
4017         Roo.log('trigger click');
4018         
4019         var target = e.getTarget();
4020         
4021         Roo.log(target.nodeName.toLowerCase());
4022         
4023         if(target.nodeName.toLowerCase() === 'i'){
4024             e.preventDefault();
4025         }
4026         
4027     },
4028     
4029     onTriggerPress  : function(e)
4030     {
4031         Roo.log('trigger press');
4032         //Roo.log(e.getTarget());
4033        // Roo.log(this.triggerEl.dom);
4034        
4035         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4036         var pel = Roo.get(e.getTarget());
4037         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4038             Roo.log('is treeview or dropdown?');
4039             return;
4040         }
4041         
4042         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4043             return;
4044         }
4045         
4046         if (this.isVisible()) {
4047             Roo.log('hide');
4048             this.hide();
4049         } else {
4050             Roo.log('show');
4051             
4052             this.show(this.triggerEl, this.align, false);
4053         }
4054         
4055         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4056             e.stopEvent();
4057         }
4058         
4059     },
4060        
4061     
4062     hideMenuItems : function()
4063     {
4064         Roo.log("hide Menu Items");
4065         if (!this.el) { 
4066             return;
4067         }
4068         
4069         this.el.select('.open',true).each(function(aa) {
4070             
4071             aa.removeClass('open');
4072          
4073         });
4074     },
4075     addxtypeChild : function (tree, cntr) {
4076         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4077           
4078         this.menuitems.add(comp);
4079         return comp;
4080
4081     },
4082     getEl : function()
4083     {
4084         Roo.log(this.el);
4085         return this.el;
4086     },
4087     
4088     clear : function()
4089     {
4090         this.getEl().dom.innerHTML = '';
4091         this.menuitems.clear();
4092     }
4093 });
4094
4095  
4096  /**
4097  * @class Roo.bootstrap.menu.Item
4098  * @extends Roo.bootstrap.Component
4099  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4100  * @parent Roo.bootstrap.menu.Menu
4101  * @licence LGPL
4102  * Bootstrap MenuItem class
4103  * 
4104  * @cfg {String} html the menu label
4105  * @cfg {String} href the link
4106  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4107  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4108  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4109  * @cfg {String} fa favicon to show on left of menu item.
4110  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4111  * 
4112  * 
4113  * @constructor
4114  * Create a new MenuItem
4115  * @param {Object} config The config object
4116  */
4117
4118
4119 Roo.bootstrap.menu.Item = function(config){
4120     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4121     this.addEvents({
4122         // raw events
4123         /**
4124          * @event click
4125          * The raw click event for the entire grid.
4126          * @param {Roo.bootstrap.menu.Item} this
4127          * @param {Roo.EventObject} e
4128          */
4129         "click" : true
4130     });
4131 };
4132
4133 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4134     
4135     href : false,
4136     html : false,
4137     preventDefault: false,
4138     isContainer : false,
4139     active : false,
4140     fa: false,
4141     
4142     getAutoCreate : function(){
4143         
4144         if(this.isContainer){
4145             return {
4146                 tag: 'li',
4147                 cls: 'dropdown-menu-item '
4148             };
4149         }
4150         var ctag = {
4151             tag: 'span',
4152             html: 'Link'
4153         };
4154         
4155         var anc = {
4156             tag : 'a',
4157             cls : 'dropdown-item',
4158             href : '#',
4159             cn : [  ]
4160         };
4161         
4162         if (this.fa !== false) {
4163             anc.cn.push({
4164                 tag : 'i',
4165                 cls : 'fa fa-' + this.fa
4166             });
4167         }
4168         
4169         anc.cn.push(ctag);
4170         
4171         
4172         var cfg= {
4173             tag: 'li',
4174             cls: 'dropdown-menu-item',
4175             cn: [ anc ]
4176         };
4177         if (this.parent().type == 'treeview') {
4178             cfg.cls = 'treeview-menu';
4179         }
4180         if (this.active) {
4181             cfg.cls += ' active';
4182         }
4183         
4184         
4185         
4186         anc.href = this.href || cfg.cn[0].href ;
4187         ctag.html = this.html || cfg.cn[0].html ;
4188         return cfg;
4189     },
4190     
4191     initEvents: function()
4192     {
4193         if (this.parent().type == 'treeview') {
4194             this.el.select('a').on('click', this.onClick, this);
4195         }
4196         
4197         if (this.menu) {
4198             this.menu.parentType = this.xtype;
4199             this.menu.triggerEl = this.el;
4200             this.menu = this.addxtype(Roo.apply({}, this.menu));
4201         }
4202         
4203     },
4204     onClick : function(e)
4205     {
4206         //Roo.log('item on click ');
4207         
4208         if(this.href === false || this.preventDefault){
4209             e.preventDefault();
4210         }
4211         //this.parent().hideMenuItems();
4212         
4213         this.fireEvent('click', this, e);
4214     },
4215     getEl : function()
4216     {
4217         return this.el;
4218     } 
4219 });
4220
4221  
4222
4223  
4224
4225   
4226 /**
4227  * @class Roo.bootstrap.menu.Separator
4228  * @extends Roo.bootstrap.Component
4229  * @licence LGPL
4230  * @parent Roo.bootstrap.menu.Menu
4231  * Bootstrap Separator class
4232  * 
4233  * @constructor
4234  * Create a new Separator
4235  * @param {Object} config The config object
4236  */
4237
4238
4239 Roo.bootstrap.menu.Separator = function(config){
4240     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4241 };
4242
4243 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4244     
4245     getAutoCreate : function(){
4246         var cfg = {
4247             tag : 'li',
4248             cls: 'dropdown-divider divider'
4249         };
4250         
4251         return cfg;
4252     }
4253    
4254 });
4255
4256  
4257
4258  
4259 /*
4260 * Licence: LGPL
4261 */
4262
4263 /**
4264  * @class Roo.bootstrap.Modal
4265  * @extends Roo.bootstrap.Component
4266  * @parent none builder
4267  * @children Roo.bootstrap.Component
4268  * Bootstrap Modal class
4269  * @cfg {String} title Title of dialog
4270  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4271  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4272  * @cfg {Boolean} specificTitle default false
4273  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4274  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4275  * @cfg {Boolean} animate default true
4276  * @cfg {Boolean} allow_close default true
4277  * @cfg {Boolean} fitwindow default false
4278  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4279  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4280  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4281  * @cfg {String} size (sm|lg|xl) default empty
4282  * @cfg {Number} max_width set the max width of modal
4283  * @cfg {Boolean} editableTitle can the title be edited
4284
4285  *
4286  *
4287  * @constructor
4288  * Create a new Modal Dialog
4289  * @param {Object} config The config object
4290  */
4291
4292 Roo.bootstrap.Modal = function(config){
4293     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4294     this.addEvents({
4295         // raw events
4296         /**
4297          * @event btnclick
4298          * The raw btnclick event for the button
4299          * @param {Roo.EventObject} e
4300          */
4301         "btnclick" : true,
4302         /**
4303          * @event resize
4304          * Fire when dialog resize
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} e
4307          */
4308         "resize" : true,
4309         /**
4310          * @event titlechanged
4311          * Fire when the editable title has been changed
4312          * @param {Roo.bootstrap.Modal} this
4313          * @param {Roo.EventObject} value
4314          */
4315         "titlechanged" : true 
4316         
4317     });
4318     this.buttons = this.buttons || [];
4319
4320     if (this.tmpl) {
4321         this.tmpl = Roo.factory(this.tmpl);
4322     }
4323
4324 };
4325
4326 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4327
4328     title : 'test dialog',
4329
4330     buttons : false,
4331
4332     // set on load...
4333
4334     html: false,
4335
4336     tmp: false,
4337
4338     specificTitle: false,
4339
4340     buttonPosition: 'right',
4341
4342     allow_close : true,
4343
4344     animate : true,
4345
4346     fitwindow: false,
4347     
4348      // private
4349     dialogEl: false,
4350     bodyEl:  false,
4351     footerEl:  false,
4352     titleEl:  false,
4353     closeEl:  false,
4354
4355     size: '',
4356     
4357     max_width: 0,
4358     
4359     max_height: 0,
4360     
4361     fit_content: false,
4362     editableTitle  : false,
4363
4364     onRender : function(ct, position)
4365     {
4366         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4367
4368         if(!this.el){
4369             var cfg = Roo.apply({},  this.getAutoCreate());
4370             cfg.id = Roo.id();
4371             //if(!cfg.name){
4372             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4373             //}
4374             //if (!cfg.name.length) {
4375             //    delete cfg.name;
4376            // }
4377             if (this.cls) {
4378                 cfg.cls += ' ' + this.cls;
4379             }
4380             if (this.style) {
4381                 cfg.style = this.style;
4382             }
4383             this.el = Roo.get(document.body).createChild(cfg, position);
4384         }
4385         //var type = this.el.dom.type;
4386
4387
4388         if(this.tabIndex !== undefined){
4389             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4390         }
4391
4392         this.dialogEl = this.el.select('.modal-dialog',true).first();
4393         this.bodyEl = this.el.select('.modal-body',true).first();
4394         this.closeEl = this.el.select('.modal-header .close', true).first();
4395         this.headerEl = this.el.select('.modal-header',true).first();
4396         this.titleEl = this.el.select('.modal-title',true).first();
4397         this.footerEl = this.el.select('.modal-footer',true).first();
4398
4399         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4400         
4401         //this.el.addClass("x-dlg-modal");
4402
4403         if (this.buttons.length) {
4404             Roo.each(this.buttons, function(bb) {
4405                 var b = Roo.apply({}, bb);
4406                 b.xns = b.xns || Roo.bootstrap;
4407                 b.xtype = b.xtype || 'Button';
4408                 if (typeof(b.listeners) == 'undefined') {
4409                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4410                 }
4411
4412                 var btn = Roo.factory(b);
4413
4414                 btn.render(this.getButtonContainer());
4415
4416             },this);
4417         }
4418         // render the children.
4419         var nitems = [];
4420
4421         if(typeof(this.items) != 'undefined'){
4422             var items = this.items;
4423             delete this.items;
4424
4425             for(var i =0;i < items.length;i++) {
4426                 // we force children not to montor widnow resize  - as we do that for them.
4427                 items[i].monitorWindowResize = false;
4428                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4429             }
4430         }
4431
4432         this.items = nitems;
4433
4434         // where are these used - they used to be body/close/footer
4435
4436
4437         this.initEvents();
4438         //this.el.addClass([this.fieldClass, this.cls]);
4439
4440     },
4441
4442     getAutoCreate : function()
4443     {
4444         // we will default to modal-body-overflow - might need to remove or make optional later.
4445         var bdy = {
4446                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4447                 html : this.html || ''
4448         };
4449
4450         var title = {
4451             tag: 'h5',
4452             cls : 'modal-title',
4453             html : this.title
4454         };
4455
4456         if(this.specificTitle){ // WTF is this?
4457             title = this.title;
4458         }
4459
4460         var header = [];
4461         if (this.allow_close && Roo.bootstrap.version == 3) {
4462             header.push({
4463                 tag: 'button',
4464                 cls : 'close',
4465                 html : '&times'
4466             });
4467         }
4468
4469         header.push(title);
4470
4471         if (this.editableTitle) {
4472             header.push({
4473                 cls: 'form-control roo-editable-title d-none',
4474                 tag: 'input',
4475                 type: 'text'
4476             });
4477         }
4478         
4479         if (this.allow_close && Roo.bootstrap.version == 4) {
4480             header.push({
4481                 tag: 'button',
4482                 cls : 'close',
4483                 html : '&times'
4484             });
4485         }
4486         
4487         var size = '';
4488
4489         if(this.size.length){
4490             size = 'modal-' + this.size;
4491         }
4492         
4493         var footer = Roo.bootstrap.version == 3 ?
4494             {
4495                 cls : 'modal-footer',
4496                 cn : [
4497                     {
4498                         tag: 'div',
4499                         cls: 'btn-' + this.buttonPosition
4500                     }
4501                 ]
4502
4503             } :
4504             {  // BS4 uses mr-auto on left buttons....
4505                 cls : 'modal-footer'
4506             };
4507
4508             
4509
4510         
4511         
4512         var modal = {
4513             cls: "modal",
4514              cn : [
4515                 {
4516                     cls: "modal-dialog " + size,
4517                     cn : [
4518                         {
4519                             cls : "modal-content",
4520                             cn : [
4521                                 {
4522                                     cls : 'modal-header',
4523                                     cn : header
4524                                 },
4525                                 bdy,
4526                                 footer
4527                             ]
4528
4529                         }
4530                     ]
4531
4532                 }
4533             ]
4534         };
4535
4536         if(this.animate){
4537             modal.cls += ' fade';
4538         }
4539
4540         return modal;
4541
4542     },
4543     getChildContainer : function() {
4544
4545          return this.bodyEl;
4546
4547     },
4548     getButtonContainer : function() {
4549         
4550          return Roo.bootstrap.version == 4 ?
4551             this.el.select('.modal-footer',true).first()
4552             : this.el.select('.modal-footer div',true).first();
4553
4554     },
4555     initEvents : function()
4556     {
4557         if (this.allow_close) {
4558             this.closeEl.on('click', this.hide, this);
4559         }
4560         Roo.EventManager.onWindowResize(this.resize, this, true);
4561         if (this.editableTitle) {
4562             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4563             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4564             this.headerEditEl.on('keyup', function(e) {
4565                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4566                         this.toggleHeaderInput(false)
4567                     }
4568                 }, this);
4569             this.headerEditEl.on('blur', function(e) {
4570                 this.toggleHeaderInput(false)
4571             },this);
4572         }
4573
4574     },
4575   
4576
4577     resize : function()
4578     {
4579         this.maskEl.setSize(
4580             Roo.lib.Dom.getViewWidth(true),
4581             Roo.lib.Dom.getViewHeight(true)
4582         );
4583         
4584         if (this.fitwindow) {
4585             
4586            this.dialogEl.setStyle( { 'max-width' : '100%' });
4587             this.setSize(
4588                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4589                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4590             );
4591             return;
4592         }
4593         
4594         if(this.max_width !== 0) {
4595             
4596             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4597             
4598             if(this.height) {
4599                 this.setSize(w, this.height);
4600                 return;
4601             }
4602             
4603             if(this.max_height) {
4604                 this.setSize(w,Math.min(
4605                     this.max_height,
4606                     Roo.lib.Dom.getViewportHeight(true) - 60
4607                 ));
4608                 
4609                 return;
4610             }
4611             
4612             if(!this.fit_content) {
4613                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4614                 return;
4615             }
4616             
4617             this.setSize(w, Math.min(
4618                 60 +
4619                 this.headerEl.getHeight() + 
4620                 this.footerEl.getHeight() + 
4621                 this.getChildHeight(this.bodyEl.dom.childNodes),
4622                 Roo.lib.Dom.getViewportHeight(true) - 60)
4623             );
4624         }
4625         
4626     },
4627
4628     setSize : function(w,h)
4629     {
4630         if (!w && !h) {
4631             return;
4632         }
4633         
4634         this.resizeTo(w,h);
4635         // any layout/border etc.. resize..
4636         (function () {
4637             this.items.forEach( function(e) {
4638                 e.layout ? e.layout() : false;
4639
4640             });
4641         }).defer(100,this);
4642         
4643     },
4644
4645     show : function() {
4646
4647         if (!this.rendered) {
4648             this.render();
4649         }
4650         this.toggleHeaderInput(false);
4651         //this.el.setStyle('display', 'block');
4652         this.el.removeClass('hideing');
4653         this.el.dom.style.display='block';
4654         
4655         Roo.get(document.body).addClass('modal-open');
4656  
4657         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4658             
4659             (function(){
4660                 this.el.addClass('show');
4661                 this.el.addClass('in');
4662             }).defer(50, this);
4663         }else{
4664             this.el.addClass('show');
4665             this.el.addClass('in');
4666         }
4667
4668         // not sure how we can show data in here..
4669         //if (this.tmpl) {
4670         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4671         //}
4672
4673         Roo.get(document.body).addClass("x-body-masked");
4674         
4675         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4676         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4677         this.maskEl.dom.style.display = 'block';
4678         this.maskEl.addClass('show');
4679         
4680         
4681         this.resize();
4682         
4683         this.fireEvent('show', this);
4684
4685         // set zindex here - otherwise it appears to be ignored...
4686         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4687         
4688         
4689         // this is for children that are... layout.Border 
4690         (function () {
4691             this.items.forEach( function(e) {
4692                 e.layout ? e.layout() : false;
4693
4694             });
4695         }).defer(100,this);
4696
4697     },
4698     hide : function()
4699     {
4700         if(this.fireEvent("beforehide", this) !== false){
4701             
4702             this.maskEl.removeClass('show');
4703             
4704             this.maskEl.dom.style.display = '';
4705             Roo.get(document.body).removeClass("x-body-masked");
4706             this.el.removeClass('in');
4707             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4708
4709             if(this.animate){ // why
4710                 this.el.addClass('hideing');
4711                 this.el.removeClass('show');
4712                 (function(){
4713                     if (!this.el.hasClass('hideing')) {
4714                         return; // it's been shown again...
4715                     }
4716                     
4717                     this.el.dom.style.display='';
4718
4719                     Roo.get(document.body).removeClass('modal-open');
4720                     this.el.removeClass('hideing');
4721                 }).defer(150,this);
4722                 
4723             }else{
4724                 this.el.removeClass('show');
4725                 this.el.dom.style.display='';
4726                 Roo.get(document.body).removeClass('modal-open');
4727
4728             }
4729             this.fireEvent('hide', this);
4730         }
4731     },
4732     isVisible : function()
4733     {
4734         
4735         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4736         
4737     },
4738
4739     addButton : function(str, cb)
4740     {
4741
4742
4743         var b = Roo.apply({}, { html : str } );
4744         b.xns = b.xns || Roo.bootstrap;
4745         b.xtype = b.xtype || 'Button';
4746         if (typeof(b.listeners) == 'undefined') {
4747             b.listeners = { click : cb.createDelegate(this)  };
4748         }
4749
4750         var btn = Roo.factory(b);
4751
4752         btn.render(this.getButtonContainer());
4753
4754         return btn;
4755
4756     },
4757
4758     setDefaultButton : function(btn)
4759     {
4760         //this.el.select('.modal-footer').()
4761     },
4762
4763     resizeTo: function(w,h)
4764     {
4765         this.dialogEl.setWidth(w);
4766         
4767         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4768
4769         this.bodyEl.setHeight(h - diff);
4770         
4771         this.fireEvent('resize', this);
4772     },
4773     
4774     setContentSize  : function(w, h)
4775     {
4776
4777     },
4778     onButtonClick: function(btn,e)
4779     {
4780         //Roo.log([a,b,c]);
4781         this.fireEvent('btnclick', btn.name, e);
4782     },
4783      /**
4784      * Set the title of the Dialog
4785      * @param {String} str new Title
4786      */
4787     setTitle: function(str) {
4788         this.titleEl.dom.innerHTML = str;
4789         this.title = str;
4790     },
4791     /**
4792      * Set the body of the Dialog
4793      * @param {String} str new Title
4794      */
4795     setBody: function(str) {
4796         this.bodyEl.dom.innerHTML = str;
4797     },
4798     /**
4799      * Set the body of the Dialog using the template
4800      * @param {Obj} data - apply this data to the template and replace the body contents.
4801      */
4802     applyBody: function(obj)
4803     {
4804         if (!this.tmpl) {
4805             Roo.log("Error - using apply Body without a template");
4806             //code
4807         }
4808         this.tmpl.overwrite(this.bodyEl, obj);
4809     },
4810     
4811     getChildHeight : function(child_nodes)
4812     {
4813         if(
4814             !child_nodes ||
4815             child_nodes.length == 0
4816         ) {
4817             return 0;
4818         }
4819         
4820         var child_height = 0;
4821         
4822         for(var i = 0; i < child_nodes.length; i++) {
4823             
4824             /*
4825             * for modal with tabs...
4826             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4827                 
4828                 var layout_childs = child_nodes[i].childNodes;
4829                 
4830                 for(var j = 0; j < layout_childs.length; j++) {
4831                     
4832                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4833                         
4834                         var layout_body_childs = layout_childs[j].childNodes;
4835                         
4836                         for(var k = 0; k < layout_body_childs.length; k++) {
4837                             
4838                             if(layout_body_childs[k].classList.contains('navbar')) {
4839                                 child_height += layout_body_childs[k].offsetHeight;
4840                                 continue;
4841                             }
4842                             
4843                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4844                                 
4845                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4846                                 
4847                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4848                                     
4849                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4850                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4851                                         continue;
4852                                     }
4853                                     
4854                                 }
4855                                 
4856                             }
4857                             
4858                         }
4859                     }
4860                 }
4861                 continue;
4862             }
4863             */
4864             
4865             child_height += child_nodes[i].offsetHeight;
4866             // Roo.log(child_nodes[i].offsetHeight);
4867         }
4868         
4869         return child_height;
4870     },
4871     toggleHeaderInput : function(is_edit)
4872     {
4873         if (!this.editableTitle) {
4874             return; // not editable.
4875         }
4876         if (is_edit && this.is_header_editing) {
4877             return; // already editing..
4878         }
4879         if (is_edit) {
4880     
4881             this.headerEditEl.dom.value = this.title;
4882             this.headerEditEl.removeClass('d-none');
4883             this.headerEditEl.dom.focus();
4884             this.titleEl.addClass('d-none');
4885             
4886             this.is_header_editing = true;
4887             return
4888         }
4889         // flip back to not editing.
4890         this.title = this.headerEditEl.dom.value;
4891         this.headerEditEl.addClass('d-none');
4892         this.titleEl.removeClass('d-none');
4893         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4894         this.is_header_editing = false;
4895         this.fireEvent('titlechanged', this, this.title);
4896     
4897             
4898         
4899     }
4900
4901 });
4902
4903
4904 Roo.apply(Roo.bootstrap.Modal,  {
4905     /**
4906          * Button config that displays a single OK button
4907          * @type Object
4908          */
4909         OK :  [{
4910             name : 'ok',
4911             weight : 'primary',
4912             html : 'OK'
4913         }],
4914         /**
4915          * Button config that displays Yes and No buttons
4916          * @type Object
4917          */
4918         YESNO : [
4919             {
4920                 name  : 'no',
4921                 html : 'No'
4922             },
4923             {
4924                 name  :'yes',
4925                 weight : 'primary',
4926                 html : 'Yes'
4927             }
4928         ],
4929
4930         /**
4931          * Button config that displays OK and Cancel buttons
4932          * @type Object
4933          */
4934         OKCANCEL : [
4935             {
4936                name : 'cancel',
4937                 html : 'Cancel'
4938             },
4939             {
4940                 name : 'ok',
4941                 weight : 'primary',
4942                 html : 'OK'
4943             }
4944         ],
4945         /**
4946          * Button config that displays Yes, No and Cancel buttons
4947          * @type Object
4948          */
4949         YESNOCANCEL : [
4950             {
4951                 name : 'yes',
4952                 weight : 'primary',
4953                 html : 'Yes'
4954             },
4955             {
4956                 name : 'no',
4957                 html : 'No'
4958             },
4959             {
4960                 name : 'cancel',
4961                 html : 'Cancel'
4962             }
4963         ],
4964         
4965         zIndex : 10001
4966 });
4967
4968 /*
4969  * - LGPL
4970  *
4971  * messagebox - can be used as a replace
4972  * 
4973  */
4974 /**
4975  * @class Roo.MessageBox
4976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4977  * Example usage:
4978  *<pre><code>
4979 // Basic alert:
4980 Roo.Msg.alert('Status', 'Changes saved successfully.');
4981
4982 // Prompt for user data:
4983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4984     if (btn == 'ok'){
4985         // process text value...
4986     }
4987 });
4988
4989 // Show a dialog using config options:
4990 Roo.Msg.show({
4991    title:'Save Changes?',
4992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4993    buttons: Roo.Msg.YESNOCANCEL,
4994    fn: processResult,
4995    animEl: 'elId'
4996 });
4997 </code></pre>
4998  * @static
4999  */
5000 Roo.bootstrap.MessageBox = function(){
5001     var dlg, opt, mask, waitTimer;
5002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5003     var buttons, activeTextEl, bwidth;
5004
5005     
5006     // private
5007     var handleButton = function(button){
5008         dlg.hide();
5009         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5010     };
5011
5012     // private
5013     var handleHide = function(){
5014         if(opt && opt.cls){
5015             dlg.el.removeClass(opt.cls);
5016         }
5017         //if(waitTimer){
5018         //    Roo.TaskMgr.stop(waitTimer);
5019         //    waitTimer = null;
5020         //}
5021     };
5022
5023     // private
5024     var updateButtons = function(b){
5025         var width = 0;
5026         if(!b){
5027             buttons["ok"].hide();
5028             buttons["cancel"].hide();
5029             buttons["yes"].hide();
5030             buttons["no"].hide();
5031             dlg.footerEl.hide();
5032             
5033             return width;
5034         }
5035         dlg.footerEl.show();
5036         for(var k in buttons){
5037             if(typeof buttons[k] != "function"){
5038                 if(b[k]){
5039                     buttons[k].show();
5040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5041                     width += buttons[k].el.getWidth()+15;
5042                 }else{
5043                     buttons[k].hide();
5044                 }
5045             }
5046         }
5047         return width;
5048     };
5049
5050     // private
5051     var handleEsc = function(d, k, e){
5052         if(opt && opt.closable !== false){
5053             dlg.hide();
5054         }
5055         if(e){
5056             e.stopEvent();
5057         }
5058     };
5059
5060     return {
5061         /**
5062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5063          * @return {Roo.BasicDialog} The BasicDialog element
5064          */
5065         getDialog : function(){
5066            if(!dlg){
5067                 dlg = new Roo.bootstrap.Modal( {
5068                     //draggable: true,
5069                     //resizable:false,
5070                     //constraintoviewport:false,
5071                     //fixedcenter:true,
5072                     //collapsible : false,
5073                     //shim:true,
5074                     //modal: true,
5075                 //    width: 'auto',
5076                   //  height:100,
5077                     //buttonAlign:"center",
5078                     closeClick : function(){
5079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5080                             handleButton("no");
5081                         }else{
5082                             handleButton("cancel");
5083                         }
5084                     }
5085                 });
5086                 dlg.render();
5087                 dlg.on("hide", handleHide);
5088                 mask = dlg.mask;
5089                 //dlg.addKeyListener(27, handleEsc);
5090                 buttons = {};
5091                 this.buttons = buttons;
5092                 var bt = this.buttonText;
5093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5097                 //Roo.log(buttons);
5098                 bodyEl = dlg.bodyEl.createChild({
5099
5100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5101                         '<textarea class="roo-mb-textarea"></textarea>' +
5102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5103                 });
5104                 msgEl = bodyEl.dom.firstChild;
5105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5106                 textboxEl.enableDisplayMode();
5107                 textboxEl.addKeyListener([10,13], function(){
5108                     if(dlg.isVisible() && opt && opt.buttons){
5109                         if(opt.buttons.ok){
5110                             handleButton("ok");
5111                         }else if(opt.buttons.yes){
5112                             handleButton("yes");
5113                         }
5114                     }
5115                 });
5116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5117                 textareaEl.enableDisplayMode();
5118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5119                 progressEl.enableDisplayMode();
5120                 
5121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5122                 var pf = progressEl.dom.firstChild;
5123                 if (pf) {
5124                     pp = Roo.get(pf.firstChild);
5125                     pp.setHeight(pf.offsetHeight);
5126                 }
5127                 
5128             }
5129             return dlg;
5130         },
5131
5132         /**
5133          * Updates the message box body text
5134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5135          * the XHTML-compliant non-breaking space character '&amp;#160;')
5136          * @return {Roo.MessageBox} This message box
5137          */
5138         updateText : function(text)
5139         {
5140             if(!dlg.isVisible() && !opt.width){
5141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5143             }
5144             msgEl.innerHTML = text || '&#160;';
5145       
5146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5148             var w = Math.max(
5149                     Math.min(opt.width || cw , this.maxWidth), 
5150                     Math.max(opt.minWidth || this.minWidth, bwidth)
5151             );
5152             if(opt.prompt){
5153                 activeTextEl.setWidth(w);
5154             }
5155             if(dlg.isVisible()){
5156                 dlg.fixedcenter = false;
5157             }
5158             // to big, make it scroll. = But as usual stupid IE does not support
5159             // !important..
5160             
5161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5164             } else {
5165                 bodyEl.dom.style.height = '';
5166                 bodyEl.dom.style.overflowY = '';
5167             }
5168             if (cw > w) {
5169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5170             } else {
5171                 bodyEl.dom.style.overflowX = '';
5172             }
5173             
5174             dlg.setContentSize(w, bodyEl.getHeight());
5175             if(dlg.isVisible()){
5176                 dlg.fixedcenter = true;
5177             }
5178             return this;
5179         },
5180
5181         /**
5182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5186          * @return {Roo.MessageBox} This message box
5187          */
5188         updateProgress : function(value, text){
5189             if(text){
5190                 this.updateText(text);
5191             }
5192             
5193             if (pp) { // weird bug on my firefox - for some reason this is not defined
5194                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5195                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5196             }
5197             return this;
5198         },        
5199
5200         /**
5201          * Returns true if the message box is currently displayed
5202          * @return {Boolean} True if the message box is visible, else false
5203          */
5204         isVisible : function(){
5205             return dlg && dlg.isVisible();  
5206         },
5207
5208         /**
5209          * Hides the message box if it is displayed
5210          */
5211         hide : function(){
5212             if(this.isVisible()){
5213                 dlg.hide();
5214             }  
5215         },
5216
5217         /**
5218          * Displays a new message box, or reinitializes an existing message box, based on the config options
5219          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5220          * The following config object properties are supported:
5221          * <pre>
5222 Property    Type             Description
5223 ----------  ---------------  ------------------------------------------------------------------------------------
5224 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5225                                    closes (defaults to undefined)
5226 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5227                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5228 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5229                                    progress and wait dialogs will ignore this property and always hide the
5230                                    close button as they can only be closed programmatically.
5231 cls               String           A custom CSS class to apply to the message box element
5232 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5233                                    displayed (defaults to 75)
5234 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5235                                    function will be btn (the name of the button that was clicked, if applicable,
5236                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5237                                    Progress and wait dialogs will ignore this option since they do not respond to
5238                                    user actions and can only be closed programmatically, so any required function
5239                                    should be called by the same code after it closes the dialog.
5240 icon              String           A CSS class that provides a background image to be used as an icon for
5241                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5242 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5243 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5244 modal             Boolean          False to allow user interaction with the page while the message box is
5245                                    displayed (defaults to true)
5246 msg               String           A string that will replace the existing message box body text (defaults
5247                                    to the XHTML-compliant non-breaking space character '&#160;')
5248 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5249 progress          Boolean          True to display a progress bar (defaults to false)
5250 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5251 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5252 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5253 title             String           The title text
5254 value             String           The string value to set into the active textbox element if displayed
5255 wait              Boolean          True to display a progress bar (defaults to false)
5256 width             Number           The width of the dialog in pixels
5257 </pre>
5258          *
5259          * Example usage:
5260          * <pre><code>
5261 Roo.Msg.show({
5262    title: 'Address',
5263    msg: 'Please enter your address:',
5264    width: 300,
5265    buttons: Roo.MessageBox.OKCANCEL,
5266    multiline: true,
5267    fn: saveAddress,
5268    animEl: 'addAddressBtn'
5269 });
5270 </code></pre>
5271          * @param {Object} config Configuration options
5272          * @return {Roo.MessageBox} This message box
5273          */
5274         show : function(options)
5275         {
5276             
5277             // this causes nightmares if you show one dialog after another
5278             // especially on callbacks..
5279              
5280             if(this.isVisible()){
5281                 
5282                 this.hide();
5283                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5284                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5285                 Roo.log("New Dialog Message:" +  options.msg )
5286                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5287                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5288                 
5289             }
5290             var d = this.getDialog();
5291             opt = options;
5292             d.setTitle(opt.title || "&#160;");
5293             d.closeEl.setDisplayed(opt.closable !== false);
5294             activeTextEl = textboxEl;
5295             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5296             if(opt.prompt){
5297                 if(opt.multiline){
5298                     textboxEl.hide();
5299                     textareaEl.show();
5300                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5301                         opt.multiline : this.defaultTextHeight);
5302                     activeTextEl = textareaEl;
5303                 }else{
5304                     textboxEl.show();
5305                     textareaEl.hide();
5306                 }
5307             }else{
5308                 textboxEl.hide();
5309                 textareaEl.hide();
5310             }
5311             progressEl.setDisplayed(opt.progress === true);
5312             if (opt.progress) {
5313                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5314             }
5315             this.updateProgress(0);
5316             activeTextEl.dom.value = opt.value || "";
5317             if(opt.prompt){
5318                 dlg.setDefaultButton(activeTextEl);
5319             }else{
5320                 var bs = opt.buttons;
5321                 var db = null;
5322                 if(bs && bs.ok){
5323                     db = buttons["ok"];
5324                 }else if(bs && bs.yes){
5325                     db = buttons["yes"];
5326                 }
5327                 dlg.setDefaultButton(db);
5328             }
5329             bwidth = updateButtons(opt.buttons);
5330             this.updateText(opt.msg);
5331             if(opt.cls){
5332                 d.el.addClass(opt.cls);
5333             }
5334             d.proxyDrag = opt.proxyDrag === true;
5335             d.modal = opt.modal !== false;
5336             d.mask = opt.modal !== false ? mask : false;
5337             if(!d.isVisible()){
5338                 // force it to the end of the z-index stack so it gets a cursor in FF
5339                 document.body.appendChild(dlg.el.dom);
5340                 d.animateTarget = null;
5341                 d.show(options.animEl);
5342             }
5343             return this;
5344         },
5345
5346         /**
5347          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5348          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5349          * and closing the message box when the process is complete.
5350          * @param {String} title The title bar text
5351          * @param {String} msg The message box body text
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         progress : function(title, msg){
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: false,
5359                 progress:true,
5360                 closable:false,
5361                 minWidth: this.minProgressWidth,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5369          * If a callback function is passed it will be called after the user clicks the button, and the
5370          * id of the button that was clicked will be passed as the only parameter to the callback
5371          * (could also be the top-right close button).
5372          * @param {String} title The title bar text
5373          * @param {String} msg The message box body text
5374          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5375          * @param {Object} scope (optional) The scope of the callback function
5376          * @return {Roo.MessageBox} This message box
5377          */
5378         alert : function(title, msg, fn, scope)
5379         {
5380             this.show({
5381                 title : title,
5382                 msg : msg,
5383                 buttons: this.OK,
5384                 fn: fn,
5385                 closable : false,
5386                 scope : scope,
5387                 modal : true
5388             });
5389             return this;
5390         },
5391
5392         /**
5393          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5394          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5395          * You are responsible for closing the message box when the process is complete.
5396          * @param {String} msg The message box body text
5397          * @param {String} title (optional) The title bar text
5398          * @return {Roo.MessageBox} This message box
5399          */
5400         wait : function(msg, title){
5401             this.show({
5402                 title : title,
5403                 msg : msg,
5404                 buttons: false,
5405                 closable:false,
5406                 progress:true,
5407                 modal:true,
5408                 width:300,
5409                 wait:true
5410             });
5411             waitTimer = Roo.TaskMgr.start({
5412                 run: function(i){
5413                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5414                 },
5415                 interval: 1000
5416             });
5417             return this;
5418         },
5419
5420         /**
5421          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5422          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5423          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @return {Roo.MessageBox} This message box
5429          */
5430         confirm : function(title, msg, fn, scope){
5431             this.show({
5432                 title : title,
5433                 msg : msg,
5434                 buttons: this.YESNO,
5435                 fn: fn,
5436                 scope : scope,
5437                 modal : true
5438             });
5439             return this;
5440         },
5441
5442         /**
5443          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5444          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5445          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5446          * (could also be the top-right close button) and the text that was entered will be passed as the two
5447          * parameters to the callback.
5448          * @param {String} title The title bar text
5449          * @param {String} msg The message box body text
5450          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5451          * @param {Object} scope (optional) The scope of the callback function
5452          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5453          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5454          * @return {Roo.MessageBox} This message box
5455          */
5456         prompt : function(title, msg, fn, scope, multiline){
5457             this.show({
5458                 title : title,
5459                 msg : msg,
5460                 buttons: this.OKCANCEL,
5461                 fn: fn,
5462                 minWidth:250,
5463                 scope : scope,
5464                 prompt:true,
5465                 multiline: multiline,
5466                 modal : true
5467             });
5468             return this;
5469         },
5470
5471         /**
5472          * Button config that displays a single OK button
5473          * @type Object
5474          */
5475         OK : {ok:true},
5476         /**
5477          * Button config that displays Yes and No buttons
5478          * @type Object
5479          */
5480         YESNO : {yes:true, no:true},
5481         /**
5482          * Button config that displays OK and Cancel buttons
5483          * @type Object
5484          */
5485         OKCANCEL : {ok:true, cancel:true},
5486         /**
5487          * Button config that displays Yes, No and Cancel buttons
5488          * @type Object
5489          */
5490         YESNOCANCEL : {yes:true, no:true, cancel:true},
5491
5492         /**
5493          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5494          * @type Number
5495          */
5496         defaultTextHeight : 75,
5497         /**
5498          * The maximum width in pixels of the message box (defaults to 600)
5499          * @type Number
5500          */
5501         maxWidth : 600,
5502         /**
5503          * The minimum width in pixels of the message box (defaults to 100)
5504          * @type Number
5505          */
5506         minWidth : 100,
5507         /**
5508          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5509          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5510          * @type Number
5511          */
5512         minProgressWidth : 250,
5513         /**
5514          * An object containing the default button text strings that can be overriden for localized language support.
5515          * Supported properties are: ok, cancel, yes and no.
5516          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5517          * @type Object
5518          */
5519         buttonText : {
5520             ok : "OK",
5521             cancel : "Cancel",
5522             yes : "Yes",
5523             no : "No"
5524         }
5525     };
5526 }();
5527
5528 /**
5529  * Shorthand for {@link Roo.MessageBox}
5530  */
5531 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5532 Roo.Msg = Roo.Msg || Roo.MessageBox;
5533 /*
5534  * - LGPL
5535  *
5536  * navbar
5537  * 
5538  */
5539
5540 /**
5541  * @class Roo.bootstrap.nav.Bar
5542  * @extends Roo.bootstrap.Component
5543  * @abstract
5544  * Bootstrap Navbar class
5545
5546  * @constructor
5547  * Create a new Navbar
5548  * @param {Object} config The config object
5549  */
5550
5551
5552 Roo.bootstrap.nav.Bar = function(config){
5553     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5554     this.addEvents({
5555         // raw events
5556         /**
5557          * @event beforetoggle
5558          * Fire before toggle the menu
5559          * @param {Roo.EventObject} e
5560          */
5561         "beforetoggle" : true
5562     });
5563 };
5564
5565 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5566     
5567     
5568    
5569     // private
5570     navItems : false,
5571     loadMask : false,
5572     
5573     
5574     getAutoCreate : function(){
5575         
5576         
5577         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5578         
5579     },
5580     
5581     initEvents :function ()
5582     {
5583         //Roo.log(this.el.select('.navbar-toggle',true));
5584         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5585         
5586         var mark = {
5587             tag: "div",
5588             cls:"x-dlg-mask"
5589         };
5590         
5591         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5592         
5593         var size = this.el.getSize();
5594         this.maskEl.setSize(size.width, size.height);
5595         this.maskEl.enableDisplayMode("block");
5596         this.maskEl.hide();
5597         
5598         if(this.loadMask){
5599             this.maskEl.show();
5600         }
5601     },
5602     
5603     
5604     getChildContainer : function()
5605     {
5606         if (this.el && this.el.select('.collapse').getCount()) {
5607             return this.el.select('.collapse',true).first();
5608         }
5609         
5610         return this.el;
5611     },
5612     
5613     mask : function()
5614     {
5615         this.maskEl.show();
5616     },
5617     
5618     unmask : function()
5619     {
5620         this.maskEl.hide();
5621     },
5622     onToggle : function()
5623     {
5624         
5625         if(this.fireEvent('beforetoggle', this) === false){
5626             return;
5627         }
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629       
5630         if (!ce.hasClass('show')) {
5631            this.expand();
5632         } else {
5633             this.collapse();
5634         }
5635         
5636         
5637     
5638     },
5639     /**
5640      * Expand the navbar pulldown 
5641      */
5642     expand : function ()
5643     {
5644        
5645         var ce = this.el.select('.navbar-collapse',true).first();
5646         if (ce.hasClass('collapsing')) {
5647             return;
5648         }
5649         ce.dom.style.height = '';
5650                // show it...
5651         ce.addClass('in'); // old...
5652         ce.removeClass('collapse');
5653         ce.addClass('show');
5654         var h = ce.getHeight();
5655         Roo.log(h);
5656         ce.removeClass('show');
5657         // at this point we should be able to see it..
5658         ce.addClass('collapsing');
5659         
5660         ce.setHeight(0); // resize it ...
5661         ce.on('transitionend', function() {
5662             //Roo.log('done transition');
5663             ce.removeClass('collapsing');
5664             ce.addClass('show');
5665             ce.removeClass('collapse');
5666
5667             ce.dom.style.height = '';
5668         }, this, { single: true} );
5669         ce.setHeight(h);
5670         ce.dom.scrollTop = 0;
5671     },
5672     /**
5673      * Collapse the navbar pulldown 
5674      */
5675     collapse : function()
5676     {
5677          var ce = this.el.select('.navbar-collapse',true).first();
5678        
5679         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5680             // it's collapsed or collapsing..
5681             return;
5682         }
5683         ce.removeClass('in'); // old...
5684         ce.setHeight(ce.getHeight());
5685         ce.removeClass('show');
5686         ce.addClass('collapsing');
5687         
5688         ce.on('transitionend', function() {
5689             ce.dom.style.height = '';
5690             ce.removeClass('collapsing');
5691             ce.addClass('collapse');
5692         }, this, { single: true} );
5693         ce.setHeight(0);
5694     }
5695     
5696     
5697     
5698 });
5699
5700
5701
5702  
5703
5704  /*
5705  * - LGPL
5706  *
5707  * navbar
5708  * 
5709  */
5710
5711 /**
5712  * @class Roo.bootstrap.nav.Simplebar
5713  * @extends Roo.bootstrap.nav.Bar
5714  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5715  * Bootstrap Sidebar class
5716  *
5717  * @cfg {Boolean} inverse is inverted color
5718  * 
5719  * @cfg {String} type (nav | pills | tabs)
5720  * @cfg {Boolean} arrangement stacked | justified
5721  * @cfg {String} align (left | right) alignment
5722  * 
5723  * @cfg {Boolean} main (true|false) main nav bar? default false
5724  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5725  * 
5726  * @cfg {String} tag (header|footer|nav|div) default is nav 
5727
5728  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5729  * 
5730  * 
5731  * @constructor
5732  * Create a new Sidebar
5733  * @param {Object} config The config object
5734  */
5735
5736
5737 Roo.bootstrap.nav.Simplebar = function(config){
5738     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5739 };
5740
5741 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5742     
5743     inverse: false,
5744     
5745     type: false,
5746     arrangement: '',
5747     align : false,
5748     
5749     weight : 'light',
5750     
5751     main : false,
5752     
5753     
5754     tag : false,
5755     
5756     
5757     getAutoCreate : function(){
5758         
5759         
5760         var cfg = {
5761             tag : this.tag || 'div',
5762             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5763         };
5764         if (['light','white'].indexOf(this.weight) > -1) {
5765             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5766         }
5767         cfg.cls += ' bg-' + this.weight;
5768         
5769         if (this.inverse) {
5770             cfg.cls += ' navbar-inverse';
5771             
5772         }
5773         
5774         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5775         
5776         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5777             return cfg;
5778         }
5779         
5780         
5781     
5782         
5783         cfg.cn = [
5784             {
5785                 cls: 'nav nav-' + this.xtype,
5786                 tag : 'ul'
5787             }
5788         ];
5789         
5790          
5791         this.type = this.type || 'nav';
5792         if (['tabs','pills'].indexOf(this.type) != -1) {
5793             cfg.cn[0].cls += ' nav-' + this.type
5794         
5795         
5796         } else {
5797             if (this.type!=='nav') {
5798                 Roo.log('nav type must be nav/tabs/pills')
5799             }
5800             cfg.cn[0].cls += ' navbar-nav'
5801         }
5802         
5803         
5804         
5805         
5806         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5807             cfg.cn[0].cls += ' nav-' + this.arrangement;
5808         }
5809         
5810         
5811         if (this.align === 'right') {
5812             cfg.cn[0].cls += ' navbar-right';
5813         }
5814         
5815         
5816         
5817         
5818         return cfg;
5819     
5820         
5821     }
5822     
5823     
5824     
5825 });
5826
5827
5828
5829  
5830
5831  
5832        /*
5833  * - LGPL
5834  *
5835  * navbar
5836  * navbar-fixed-top
5837  * navbar-expand-md  fixed-top 
5838  */
5839
5840 /**
5841  * @class Roo.bootstrap.nav.Headerbar
5842  * @extends Roo.bootstrap.nav.Simplebar
5843  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5844  * Bootstrap Sidebar class
5845  *
5846  * @cfg {String} brand what is brand
5847  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5848  * @cfg {String} brand_href href of the brand
5849  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5850  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5851  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5852  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5853  * 
5854  * @constructor
5855  * Create a new Sidebar
5856  * @param {Object} config The config object
5857  */
5858
5859
5860 Roo.bootstrap.nav.Headerbar = function(config){
5861     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5862       
5863 };
5864
5865 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5866     
5867     position: '',
5868     brand: '',
5869     brand_href: false,
5870     srButton : true,
5871     autohide : false,
5872     desktopCenter : false,
5873    
5874     
5875     getAutoCreate : function(){
5876         
5877         var   cfg = {
5878             tag: this.nav || 'nav',
5879             cls: 'navbar navbar-expand-md',
5880             role: 'navigation',
5881             cn: []
5882         };
5883         
5884         var cn = cfg.cn;
5885         if (this.desktopCenter) {
5886             cn.push({cls : 'container', cn : []});
5887             cn = cn[0].cn;
5888         }
5889         
5890         if(this.srButton){
5891             var btn = {
5892                 tag: 'button',
5893                 type: 'button',
5894                 cls: 'navbar-toggle navbar-toggler',
5895                 'data-toggle': 'collapse',
5896                 cn: [
5897                     {
5898                         tag: 'span',
5899                         cls: 'sr-only',
5900                         html: 'Toggle navigation'
5901                     },
5902                     {
5903                         tag: 'span',
5904                         cls: 'icon-bar navbar-toggler-icon'
5905                     },
5906                     {
5907                         tag: 'span',
5908                         cls: 'icon-bar'
5909                     },
5910                     {
5911                         tag: 'span',
5912                         cls: 'icon-bar'
5913                     }
5914                 ]
5915             };
5916             
5917             cn.push( Roo.bootstrap.version == 4 ? btn : {
5918                 tag: 'div',
5919                 cls: 'navbar-header',
5920                 cn: [
5921                     btn
5922                 ]
5923             });
5924         }
5925         
5926         cn.push({
5927             tag: 'div',
5928             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5929             cn : []
5930         });
5931         
5932         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5933         
5934         if (['light','white'].indexOf(this.weight) > -1) {
5935             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5936         }
5937         cfg.cls += ' bg-' + this.weight;
5938         
5939         
5940         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5941             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5942             
5943             // tag can override this..
5944             
5945             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5946         }
5947         
5948         if (this.brand !== '') {
5949             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5950             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5951                 tag: 'a',
5952                 href: this.brand_href ? this.brand_href : '#',
5953                 cls: 'navbar-brand',
5954                 cn: [
5955                 this.brand
5956                 ]
5957             });
5958         }
5959         
5960         if(this.main){
5961             cfg.cls += ' main-nav';
5962         }
5963         
5964         
5965         return cfg;
5966
5967         
5968     },
5969     getHeaderChildContainer : function()
5970     {
5971         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5972             return this.el.select('.navbar-header',true).first();
5973         }
5974         
5975         return this.getChildContainer();
5976     },
5977     
5978     getChildContainer : function()
5979     {
5980          
5981         return this.el.select('.roo-navbar-collapse',true).first();
5982          
5983         
5984     },
5985     
5986     initEvents : function()
5987     {
5988         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5989         
5990         if (this.autohide) {
5991             
5992             var prevScroll = 0;
5993             var ft = this.el;
5994             
5995             Roo.get(document).on('scroll',function(e) {
5996                 var ns = Roo.get(document).getScroll().top;
5997                 var os = prevScroll;
5998                 prevScroll = ns;
5999                 
6000                 if(ns > os){
6001                     ft.removeClass('slideDown');
6002                     ft.addClass('slideUp');
6003                     return;
6004                 }
6005                 ft.removeClass('slideUp');
6006                 ft.addClass('slideDown');
6007                  
6008               
6009           },this);
6010         }
6011     }    
6012     
6013 });
6014
6015
6016
6017  
6018
6019  /*
6020  * - LGPL
6021  *
6022  * navbar
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.nav.Sidebar
6028  * @extends Roo.bootstrap.nav.Bar
6029  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6030  * Bootstrap Sidebar class
6031  * 
6032  * @constructor
6033  * Create a new Sidebar
6034  * @param {Object} config The config object
6035  */
6036
6037
6038 Roo.bootstrap.nav.Sidebar = function(config){
6039     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6040 };
6041
6042 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6043     
6044     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6045     
6046     getAutoCreate : function(){
6047         
6048         
6049         return  {
6050             tag: 'div',
6051             cls: 'sidebar sidebar-nav'
6052         };
6053     
6054         
6055     }
6056     
6057     
6058     
6059 });
6060
6061
6062
6063  
6064
6065  /*
6066  * - LGPL
6067  *
6068  * nav group
6069  * 
6070  */
6071
6072 /**
6073  * @class Roo.bootstrap.nav.Group
6074  * @extends Roo.bootstrap.Component
6075  * @children Roo.bootstrap.nav.Item
6076  * Bootstrap NavGroup class
6077  * @cfg {String} align (left|right)
6078  * @cfg {Boolean} inverse
6079  * @cfg {String} type (nav|pills|tab) default nav
6080  * @cfg {String} navId - reference Id for navbar.
6081  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6082  * 
6083  * @constructor
6084  * Create a new nav group
6085  * @param {Object} config The config object
6086  */
6087
6088 Roo.bootstrap.nav.Group = function(config){
6089     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6090     this.navItems = [];
6091    
6092     Roo.bootstrap.nav.Group.register(this);
6093      this.addEvents({
6094         /**
6095              * @event changed
6096              * Fires when the active item changes
6097              * @param {Roo.bootstrap.nav.Group} this
6098              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6099              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6100          */
6101         'changed': true
6102      });
6103     
6104 };
6105
6106 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6107     
6108     align: '',
6109     inverse: false,
6110     form: false,
6111     type: 'nav',
6112     navId : '',
6113     // private
6114     pilltype : true,
6115     
6116     navItems : false, 
6117     
6118     getAutoCreate : function()
6119     {
6120         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6121         
6122         cfg = {
6123             tag : 'ul',
6124             cls: 'nav' 
6125         };
6126         if (Roo.bootstrap.version == 4) {
6127             if (['tabs','pills'].indexOf(this.type) != -1) {
6128                 cfg.cls += ' nav-' + this.type; 
6129             } else {
6130                 // trying to remove so header bar can right align top?
6131                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6132                     // do not use on header bar... 
6133                     cfg.cls += ' navbar-nav';
6134                 }
6135             }
6136             
6137         } else {
6138             if (['tabs','pills'].indexOf(this.type) != -1) {
6139                 cfg.cls += ' nav-' + this.type
6140             } else {
6141                 if (this.type !== 'nav') {
6142                     Roo.log('nav type must be nav/tabs/pills')
6143                 }
6144                 cfg.cls += ' navbar-nav'
6145             }
6146         }
6147         
6148         if (this.parent() && this.parent().sidebar) {
6149             cfg = {
6150                 tag: 'ul',
6151                 cls: 'dashboard-menu sidebar-menu'
6152             };
6153             
6154             return cfg;
6155         }
6156         
6157         if (this.form === true) {
6158             cfg = {
6159                 tag: 'form',
6160                 cls: 'navbar-form form-inline'
6161             };
6162             //nav navbar-right ml-md-auto
6163             if (this.align === 'right') {
6164                 cfg.cls += ' navbar-right ml-md-auto';
6165             } else {
6166                 cfg.cls += ' navbar-left';
6167             }
6168         }
6169         
6170         if (this.align === 'right') {
6171             cfg.cls += ' navbar-right ml-md-auto';
6172         } else {
6173             cfg.cls += ' mr-auto';
6174         }
6175         
6176         if (this.inverse) {
6177             cfg.cls += ' navbar-inverse';
6178             
6179         }
6180         
6181         
6182         return cfg;
6183     },
6184     /**
6185     * sets the active Navigation item
6186     * @param {Roo.bootstrap.nav.Item} the new current navitem
6187     */
6188     setActiveItem : function(item)
6189     {
6190         var prev = false;
6191         Roo.each(this.navItems, function(v){
6192             if (v == item) {
6193                 return ;
6194             }
6195             if (v.isActive()) {
6196                 v.setActive(false, true);
6197                 prev = v;
6198                 
6199             }
6200             
6201         });
6202
6203         item.setActive(true, true);
6204         this.fireEvent('changed', this, item, prev);
6205         
6206         
6207     },
6208     /**
6209     * gets the active Navigation item
6210     * @return {Roo.bootstrap.nav.Item} the current navitem
6211     */
6212     getActive : function()
6213     {
6214         
6215         var prev = false;
6216         Roo.each(this.navItems, function(v){
6217             
6218             if (v.isActive()) {
6219                 prev = v;
6220                 
6221             }
6222             
6223         });
6224         return prev;
6225     },
6226     
6227     indexOfNav : function()
6228     {
6229         
6230         var prev = false;
6231         Roo.each(this.navItems, function(v,i){
6232             
6233             if (v.isActive()) {
6234                 prev = i;
6235                 
6236             }
6237             
6238         });
6239         return prev;
6240     },
6241     /**
6242     * adds a Navigation item
6243     * @param {Roo.bootstrap.nav.Item} the navitem to add
6244     */
6245     addItem : function(cfg)
6246     {
6247         if (this.form && Roo.bootstrap.version == 4) {
6248             cfg.tag = 'div';
6249         }
6250         var cn = new Roo.bootstrap.nav.Item(cfg);
6251         this.register(cn);
6252         cn.parentId = this.id;
6253         cn.onRender(this.el, null);
6254         return cn;
6255     },
6256     /**
6257     * register a Navigation item
6258     * @param {Roo.bootstrap.nav.Item} the navitem to add
6259     */
6260     register : function(item)
6261     {
6262         this.navItems.push( item);
6263         item.navId = this.navId;
6264     
6265     },
6266     
6267     /**
6268     * clear all the Navigation item
6269     */
6270    
6271     clearAll : function()
6272     {
6273         this.navItems = [];
6274         this.el.dom.innerHTML = '';
6275     },
6276     
6277     getNavItem: function(tabId)
6278     {
6279         var ret = false;
6280         Roo.each(this.navItems, function(e) {
6281             if (e.tabId == tabId) {
6282                ret =  e;
6283                return false;
6284             }
6285             return true;
6286             
6287         });
6288         return ret;
6289     },
6290     
6291     setActiveNext : function()
6292     {
6293         var i = this.indexOfNav(this.getActive());
6294         if (i > this.navItems.length) {
6295             return;
6296         }
6297         this.setActiveItem(this.navItems[i+1]);
6298     },
6299     setActivePrev : function()
6300     {
6301         var i = this.indexOfNav(this.getActive());
6302         if (i  < 1) {
6303             return;
6304         }
6305         this.setActiveItem(this.navItems[i-1]);
6306     },
6307     clearWasActive : function(except) {
6308         Roo.each(this.navItems, function(e) {
6309             if (e.tabId != except.tabId && e.was_active) {
6310                e.was_active = false;
6311                return false;
6312             }
6313             return true;
6314             
6315         });
6316     },
6317     getWasActive : function ()
6318     {
6319         var r = false;
6320         Roo.each(this.navItems, function(e) {
6321             if (e.was_active) {
6322                r = e;
6323                return false;
6324             }
6325             return true;
6326             
6327         });
6328         return r;
6329     }
6330     
6331     
6332 });
6333
6334  
6335 Roo.apply(Roo.bootstrap.nav.Group, {
6336     
6337     groups: {},
6338      /**
6339     * register a Navigation Group
6340     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6341     */
6342     register : function(navgrp)
6343     {
6344         this.groups[navgrp.navId] = navgrp;
6345         
6346     },
6347     /**
6348     * fetch a Navigation Group based on the navigation ID
6349     * @param {string} the navgroup to add
6350     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6351     */
6352     get: function(navId) {
6353         if (typeof(this.groups[navId]) == 'undefined') {
6354             return false;
6355             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6356         }
6357         return this.groups[navId] ;
6358     }
6359     
6360     
6361     
6362 });
6363
6364  /**
6365  * @class Roo.bootstrap.nav.Item
6366  * @extends Roo.bootstrap.Component
6367  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6368  * @parent Roo.bootstrap.nav.Group
6369  * @licence LGPL
6370  * Bootstrap Navbar.NavItem class
6371  * 
6372  * @cfg {String} href  link to
6373  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6374  * @cfg {Boolean} button_outline show and outlined button
6375  * @cfg {String} html content of button
6376  * @cfg {String} badge text inside badge
6377  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6378  * @cfg {String} glyphicon DEPRICATED - use fa
6379  * @cfg {String} icon DEPRICATED - use fa
6380  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6381  * @cfg {Boolean} active Is item active
6382  * @cfg {Boolean} disabled Is item disabled
6383  * @cfg {String} linkcls  Link Class
6384  * @cfg {Boolean} preventDefault (true | false) default false
6385  * @cfg {String} tabId the tab that this item activates.
6386  * @cfg {String} tagtype (a|span) render as a href or span?
6387  * @cfg {Boolean} animateRef (true|false) link to element default false  
6388  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6389   
6390  * @constructor
6391  * Create a new Navbar Item
6392  * @param {Object} config The config object
6393  */
6394 Roo.bootstrap.nav.Item = function(config){
6395     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6396     this.addEvents({
6397         // raw events
6398         /**
6399          * @event click
6400          * The raw click event for the entire grid.
6401          * @param {Roo.EventObject} e
6402          */
6403         "click" : true,
6404          /**
6405             * @event changed
6406             * Fires when the active item active state changes
6407             * @param {Roo.bootstrap.nav.Item} this
6408             * @param {boolean} state the new state
6409              
6410          */
6411         'changed': true,
6412         /**
6413             * @event scrollto
6414             * Fires when scroll to element
6415             * @param {Roo.bootstrap.nav.Item} this
6416             * @param {Object} options
6417             * @param {Roo.EventObject} e
6418              
6419          */
6420         'scrollto': true
6421     });
6422    
6423 };
6424
6425 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6426     
6427     href: false,
6428     html: '',
6429     badge: '',
6430     icon: false,
6431     fa : false,
6432     glyphicon: false,
6433     active: false,
6434     preventDefault : false,
6435     tabId : false,
6436     tagtype : 'a',
6437     tag: 'li',
6438     disabled : false,
6439     animateRef : false,
6440     was_active : false,
6441     button_weight : '',
6442     button_outline : false,
6443     linkcls : '',
6444     navLink: false,
6445     
6446     getAutoCreate : function(){
6447          
6448         var cfg = {
6449             tag: this.tag,
6450             cls: 'nav-item'
6451         };
6452         
6453         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6454         
6455         if (this.active) {
6456             cfg.cls +=  ' active' ;
6457         }
6458         if (this.disabled) {
6459             cfg.cls += ' disabled';
6460         }
6461         
6462         // BS4 only?
6463         if (this.button_weight.length) {
6464             cfg.tag = this.href ? 'a' : 'button';
6465             cfg.html = this.html || '';
6466             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6467             if (this.href) {
6468                 cfg.href = this.href;
6469             }
6470             if (this.fa) {
6471                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6472             } else {
6473                 cfg.cls += " nav-html";
6474             }
6475             
6476             // menu .. should add dropdown-menu class - so no need for carat..
6477             
6478             if (this.badge !== '') {
6479                  
6480                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6481             }
6482             return cfg;
6483         }
6484         
6485         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6486             cfg.cn = [
6487                 {
6488                     tag: this.tagtype,
6489                     href : this.href || "#",
6490                     html: this.html || '',
6491                     cls : ''
6492                 }
6493             ];
6494             if (this.tagtype == 'a') {
6495                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6496         
6497             }
6498             if (this.icon) {
6499                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6500             } else  if (this.fa) {
6501                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6502             } else if(this.glyphicon) {
6503                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6504             } else {
6505                 cfg.cn[0].cls += " nav-html";
6506             }
6507             
6508             if (this.menu) {
6509                 cfg.cn[0].html += " <span class='caret'></span>";
6510              
6511             }
6512             
6513             if (this.badge !== '') {
6514                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6515             }
6516         }
6517         
6518         
6519         
6520         return cfg;
6521     },
6522     onRender : function(ct, position)
6523     {
6524        // Roo.log("Call onRender: " + this.xtype);
6525         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6526             this.tag = 'div';
6527         }
6528         
6529         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6530         this.navLink = this.el.select('.nav-link',true).first();
6531         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6532         return ret;
6533     },
6534       
6535     
6536     initEvents: function() 
6537     {
6538         if (typeof (this.menu) != 'undefined') {
6539             this.menu.parentType = this.xtype;
6540             this.menu.triggerEl = this.el;
6541             this.menu = this.addxtype(Roo.apply({}, this.menu));
6542         }
6543         
6544         this.el.on('click', this.onClick, this);
6545         
6546         //if(this.tagtype == 'span'){
6547         //    this.el.select('span',true).on('click', this.onClick, this);
6548         //}
6549        
6550         // at this point parent should be available..
6551         this.parent().register(this);
6552     },
6553     
6554     onClick : function(e)
6555     {
6556         if (e.getTarget('.dropdown-menu-item')) {
6557             // did you click on a menu itemm.... - then don't trigger onclick..
6558             return;
6559         }
6560         
6561         if(
6562                 this.preventDefault ||
6563                                 this.href === false ||
6564                 this.href === '#' 
6565         ){
6566             //Roo.log("NavItem - prevent Default?");
6567             e.preventDefault();
6568         }
6569         
6570         if (this.disabled) {
6571             return;
6572         }
6573         
6574         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6575         if (tg && tg.transition) {
6576             Roo.log("waiting for the transitionend");
6577             return;
6578         }
6579         
6580         
6581         
6582         //Roo.log("fire event clicked");
6583         if(this.fireEvent('click', this, e) === false){
6584             return;
6585         };
6586         
6587         if(this.tagtype == 'span'){
6588             return;
6589         }
6590         
6591         //Roo.log(this.href);
6592         var ael = this.el.select('a',true).first();
6593         //Roo.log(ael);
6594         
6595         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6596             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6597             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6598                 return; // ignore... - it's a 'hash' to another page.
6599             }
6600             Roo.log("NavItem - prevent Default?");
6601             e.preventDefault();
6602             this.scrollToElement(e);
6603         }
6604         
6605         
6606         var p =  this.parent();
6607    
6608         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6609             if (typeof(p.setActiveItem) !== 'undefined') {
6610                 p.setActiveItem(this);
6611             }
6612         }
6613         
6614         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6615         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6616             // remove the collapsed menu expand...
6617             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6618         }
6619     },
6620     
6621     isActive: function () {
6622         return this.active
6623     },
6624     setActive : function(state, fire, is_was_active)
6625     {
6626         if (this.active && !state && this.navId) {
6627             this.was_active = true;
6628             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6629             if (nv) {
6630                 nv.clearWasActive(this);
6631             }
6632             
6633         }
6634         this.active = state;
6635         
6636         if (!state ) {
6637             this.el.removeClass('active');
6638             this.navLink ? this.navLink.removeClass('active') : false;
6639         } else if (!this.el.hasClass('active')) {
6640             
6641             this.el.addClass('active');
6642             if (Roo.bootstrap.version == 4 && this.navLink ) {
6643                 this.navLink.addClass('active');
6644             }
6645             
6646         }
6647         if (fire) {
6648             this.fireEvent('changed', this, state);
6649         }
6650         
6651         // show a panel if it's registered and related..
6652         
6653         if (!this.navId || !this.tabId || !state || is_was_active) {
6654             return;
6655         }
6656         
6657         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6658         if (!tg) {
6659             return;
6660         }
6661         var pan = tg.getPanelByName(this.tabId);
6662         if (!pan) {
6663             return;
6664         }
6665         // if we can not flip to new panel - go back to old nav highlight..
6666         if (false == tg.showPanel(pan)) {
6667             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6668             if (nv) {
6669                 var onav = nv.getWasActive();
6670                 if (onav) {
6671                     onav.setActive(true, false, true);
6672                 }
6673             }
6674             
6675         }
6676         
6677         
6678         
6679     },
6680      // this should not be here...
6681     setDisabled : function(state)
6682     {
6683         this.disabled = state;
6684         if (!state ) {
6685             this.el.removeClass('disabled');
6686         } else if (!this.el.hasClass('disabled')) {
6687             this.el.addClass('disabled');
6688         }
6689         
6690     },
6691     
6692     /**
6693      * Fetch the element to display the tooltip on.
6694      * @return {Roo.Element} defaults to this.el
6695      */
6696     tooltipEl : function()
6697     {
6698         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6699     },
6700     
6701     scrollToElement : function(e)
6702     {
6703         var c = document.body;
6704         
6705         /*
6706          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6707          */
6708         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6709             c = document.documentElement;
6710         }
6711         
6712         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6713         
6714         if(!target){
6715             return;
6716         }
6717
6718         var o = target.calcOffsetsTo(c);
6719         
6720         var options = {
6721             target : target,
6722             value : o[1]
6723         };
6724         
6725         this.fireEvent('scrollto', this, options, e);
6726         
6727         Roo.get(c).scrollTo('top', options.value, true);
6728         
6729         return;
6730     },
6731     /**
6732      * Set the HTML (text content) of the item
6733      * @param {string} html  content for the nav item
6734      */
6735     setHtml : function(html)
6736     {
6737         this.html = html;
6738         this.htmlEl.dom.innerHTML = html;
6739         
6740     } 
6741 });
6742  
6743
6744  /*
6745  * - LGPL
6746  *
6747  * sidebar item
6748  *
6749  *  li
6750  *    <span> icon </span>
6751  *    <span> text </span>
6752  *    <span>badge </span>
6753  */
6754
6755 /**
6756  * @class Roo.bootstrap.nav.SidebarItem
6757  * @extends Roo.bootstrap.nav.Item
6758  * Bootstrap Navbar.NavSidebarItem class
6759  * 
6760  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6761  * {Boolean} open is the menu open
6762  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6763  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6764  * {String} buttonSize (sm|md|lg)the extra classes for the button
6765  * {Boolean} showArrow show arrow next to the text (default true)
6766  * @constructor
6767  * Create a new Navbar Button
6768  * @param {Object} config The config object
6769  */
6770 Roo.bootstrap.nav.SidebarItem = function(config){
6771     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6772     this.addEvents({
6773         // raw events
6774         /**
6775          * @event click
6776          * The raw click event for the entire grid.
6777          * @param {Roo.EventObject} e
6778          */
6779         "click" : true,
6780          /**
6781             * @event changed
6782             * Fires when the active item active state changes
6783             * @param {Roo.bootstrap.nav.SidebarItem} this
6784             * @param {boolean} state the new state
6785              
6786          */
6787         'changed': true
6788     });
6789    
6790 };
6791
6792 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6793     
6794     badgeWeight : 'default',
6795     
6796     open: false,
6797     
6798     buttonView : false,
6799     
6800     buttonWeight : 'default',
6801     
6802     buttonSize : 'md',
6803     
6804     showArrow : true,
6805     
6806     getAutoCreate : function(){
6807         
6808         
6809         var a = {
6810                 tag: 'a',
6811                 href : this.href || '#',
6812                 cls: '',
6813                 html : '',
6814                 cn : []
6815         };
6816         
6817         if(this.buttonView){
6818             a = {
6819                 tag: 'button',
6820                 href : this.href || '#',
6821                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6822                 html : this.html,
6823                 cn : []
6824             };
6825         }
6826         
6827         var cfg = {
6828             tag: 'li',
6829             cls: '',
6830             cn: [ a ]
6831         };
6832         
6833         if (this.active) {
6834             cfg.cls += ' active';
6835         }
6836         
6837         if (this.disabled) {
6838             cfg.cls += ' disabled';
6839         }
6840         if (this.open) {
6841             cfg.cls += ' open x-open';
6842         }
6843         // left icon..
6844         if (this.glyphicon || this.icon) {
6845             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6846             a.cn.push({ tag : 'i', cls : c }) ;
6847         }
6848         
6849         if(!this.buttonView){
6850             var span = {
6851                 tag: 'span',
6852                 html : this.html || ''
6853             };
6854
6855             a.cn.push(span);
6856             
6857         }
6858         
6859         if (this.badge !== '') {
6860             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6861         }
6862         
6863         if (this.menu) {
6864             
6865             if(this.showArrow){
6866                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6867             }
6868             
6869             a.cls += ' dropdown-toggle treeview' ;
6870         }
6871         
6872         return cfg;
6873     },
6874     
6875     initEvents : function()
6876     { 
6877         if (typeof (this.menu) != 'undefined') {
6878             this.menu.parentType = this.xtype;
6879             this.menu.triggerEl = this.el;
6880             this.menu = this.addxtype(Roo.apply({}, this.menu));
6881         }
6882         
6883         this.el.on('click', this.onClick, this);
6884         
6885         if(this.badge !== ''){
6886             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6887         }
6888         
6889     },
6890     
6891     onClick : function(e)
6892     {
6893         if(this.disabled){
6894             e.preventDefault();
6895             return;
6896         }
6897         
6898         if(this.preventDefault){
6899             e.preventDefault();
6900         }
6901         
6902         this.fireEvent('click', this, e);
6903     },
6904     
6905     disable : function()
6906     {
6907         this.setDisabled(true);
6908     },
6909     
6910     enable : function()
6911     {
6912         this.setDisabled(false);
6913     },
6914     
6915     setDisabled : function(state)
6916     {
6917         if(this.disabled == state){
6918             return;
6919         }
6920         
6921         this.disabled = state;
6922         
6923         if (state) {
6924             this.el.addClass('disabled');
6925             return;
6926         }
6927         
6928         this.el.removeClass('disabled');
6929         
6930         return;
6931     },
6932     
6933     setActive : function(state)
6934     {
6935         if(this.active == state){
6936             return;
6937         }
6938         
6939         this.active = state;
6940         
6941         if (state) {
6942             this.el.addClass('active');
6943             return;
6944         }
6945         
6946         this.el.removeClass('active');
6947         
6948         return;
6949     },
6950     
6951     isActive: function () 
6952     {
6953         return this.active;
6954     },
6955     
6956     setBadge : function(str)
6957     {
6958         if(!this.badgeEl){
6959             return;
6960         }
6961         
6962         this.badgeEl.dom.innerHTML = str;
6963     }
6964     
6965    
6966      
6967  
6968 });
6969  
6970
6971  /*
6972  * - LGPL
6973  *
6974  * nav progress bar
6975  * 
6976  */
6977
6978 /**
6979  * @class Roo.bootstrap.nav.ProgressBar
6980  * @extends Roo.bootstrap.Component
6981  * @children Roo.bootstrap.nav.ProgressBarItem
6982  * Bootstrap NavProgressBar class
6983  * 
6984  * @constructor
6985  * Create a new nav progress bar - a bar indicating step along a process
6986  * @param {Object} config The config object
6987  */
6988
6989 Roo.bootstrap.nav.ProgressBar = function(config){
6990     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6991
6992     this.bullets = this.bullets || [];
6993    
6994 //    Roo.bootstrap.nav.ProgressBar.register(this);
6995      this.addEvents({
6996         /**
6997              * @event changed
6998              * Fires when the active item changes
6999              * @param {Roo.bootstrap.nav.ProgressBar} this
7000              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7001              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7002          */
7003         'changed': true
7004      });
7005     
7006 };
7007
7008 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7009     /**
7010      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7011      * Bullets for the Nav Progress bar for the toolbar
7012      */
7013     bullets : [],
7014     barItems : [],
7015     
7016     getAutoCreate : function()
7017     {
7018         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag : 'div',
7022             cls : 'roo-navigation-bar-group',
7023             cn : [
7024                 {
7025                     tag : 'div',
7026                     cls : 'roo-navigation-top-bar'
7027                 },
7028                 {
7029                     tag : 'div',
7030                     cls : 'roo-navigation-bullets-bar',
7031                     cn : [
7032                         {
7033                             tag : 'ul',
7034                             cls : 'roo-navigation-bar'
7035                         }
7036                     ]
7037                 },
7038                 
7039                 {
7040                     tag : 'div',
7041                     cls : 'roo-navigation-bottom-bar'
7042                 }
7043             ]
7044             
7045         };
7046         
7047         return cfg;
7048         
7049     },
7050     
7051     initEvents: function() 
7052     {
7053         
7054     },
7055     
7056     onRender : function(ct, position) 
7057     {
7058         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7059         
7060         if(this.bullets.length){
7061             Roo.each(this.bullets, function(b){
7062                this.addItem(b);
7063             }, this);
7064         }
7065         
7066         this.format();
7067         
7068     },
7069     
7070     addItem : function(cfg)
7071     {
7072         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7073         
7074         item.parentId = this.id;
7075         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7076         
7077         if(cfg.html){
7078             var top = new Roo.bootstrap.Element({
7079                 tag : 'div',
7080                 cls : 'roo-navigation-bar-text'
7081             });
7082             
7083             var bottom = new Roo.bootstrap.Element({
7084                 tag : 'div',
7085                 cls : 'roo-navigation-bar-text'
7086             });
7087             
7088             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7089             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7090             
7091             var topText = new Roo.bootstrap.Element({
7092                 tag : 'span',
7093                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7094             });
7095             
7096             var bottomText = new Roo.bootstrap.Element({
7097                 tag : 'span',
7098                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7099             });
7100             
7101             topText.onRender(top.el, null);
7102             bottomText.onRender(bottom.el, null);
7103             
7104             item.topEl = top;
7105             item.bottomEl = bottom;
7106         }
7107         
7108         this.barItems.push(item);
7109         
7110         return item;
7111     },
7112     
7113     getActive : function()
7114     {
7115         var active = false;
7116         
7117         Roo.each(this.barItems, function(v){
7118             
7119             if (!v.isActive()) {
7120                 return;
7121             }
7122             
7123             active = v;
7124             return false;
7125             
7126         });
7127         
7128         return active;
7129     },
7130     
7131     setActiveItem : function(item)
7132     {
7133         var prev = false;
7134         
7135         Roo.each(this.barItems, function(v){
7136             if (v.rid == item.rid) {
7137                 return ;
7138             }
7139             
7140             if (v.isActive()) {
7141                 v.setActive(false);
7142                 prev = v;
7143             }
7144         });
7145
7146         item.setActive(true);
7147         
7148         this.fireEvent('changed', this, item, prev);
7149     },
7150     
7151     getBarItem: function(rid)
7152     {
7153         var ret = false;
7154         
7155         Roo.each(this.barItems, function(e) {
7156             if (e.rid != rid) {
7157                 return;
7158             }
7159             
7160             ret =  e;
7161             return false;
7162         });
7163         
7164         return ret;
7165     },
7166     
7167     indexOfItem : function(item)
7168     {
7169         var index = false;
7170         
7171         Roo.each(this.barItems, function(v, i){
7172             
7173             if (v.rid != item.rid) {
7174                 return;
7175             }
7176             
7177             index = i;
7178             return false
7179         });
7180         
7181         return index;
7182     },
7183     
7184     setActiveNext : function()
7185     {
7186         var i = this.indexOfItem(this.getActive());
7187         
7188         if (i > this.barItems.length) {
7189             return;
7190         }
7191         
7192         this.setActiveItem(this.barItems[i+1]);
7193     },
7194     
7195     setActivePrev : function()
7196     {
7197         var i = this.indexOfItem(this.getActive());
7198         
7199         if (i  < 1) {
7200             return;
7201         }
7202         
7203         this.setActiveItem(this.barItems[i-1]);
7204     },
7205     
7206     format : function()
7207     {
7208         if(!this.barItems.length){
7209             return;
7210         }
7211      
7212         var width = 100 / this.barItems.length;
7213         
7214         Roo.each(this.barItems, function(i){
7215             i.el.setStyle('width', width + '%');
7216             i.topEl.el.setStyle('width', width + '%');
7217             i.bottomEl.el.setStyle('width', width + '%');
7218         }, this);
7219         
7220     }
7221     
7222 });
7223 /*
7224  * - LGPL
7225  *
7226  * Nav Progress Item
7227  * 
7228  */
7229
7230 /**
7231  * @class Roo.bootstrap.nav.ProgressBarItem
7232  * @extends Roo.bootstrap.Component
7233  * Bootstrap NavProgressBarItem class
7234  * @cfg {String} rid the reference id
7235  * @cfg {Boolean} active (true|false) Is item active default false
7236  * @cfg {Boolean} disabled (true|false) Is item active default false
7237  * @cfg {String} html
7238  * @cfg {String} position (top|bottom) text position default bottom
7239  * @cfg {String} icon show icon instead of number
7240  * 
7241  * @constructor
7242  * Create a new NavProgressBarItem
7243  * @param {Object} config The config object
7244  */
7245 Roo.bootstrap.nav.ProgressBarItem = function(config){
7246     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7247     this.addEvents({
7248         // raw events
7249         /**
7250          * @event click
7251          * The raw click event for the entire grid.
7252          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7253          * @param {Roo.EventObject} e
7254          */
7255         "click" : true
7256     });
7257    
7258 };
7259
7260 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7261     
7262     rid : '',
7263     active : false,
7264     disabled : false,
7265     html : '',
7266     position : 'bottom',
7267     icon : false,
7268     
7269     getAutoCreate : function()
7270     {
7271         var iconCls = 'roo-navigation-bar-item-icon';
7272         
7273         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7274         
7275         var cfg = {
7276             tag: 'li',
7277             cls: 'roo-navigation-bar-item',
7278             cn : [
7279                 {
7280                     tag : 'i',
7281                     cls : iconCls
7282                 }
7283             ]
7284         };
7285         
7286         if(this.active){
7287             cfg.cls += ' active';
7288         }
7289         if(this.disabled){
7290             cfg.cls += ' disabled';
7291         }
7292         
7293         return cfg;
7294     },
7295     
7296     disable : function()
7297     {
7298         this.setDisabled(true);
7299     },
7300     
7301     enable : function()
7302     {
7303         this.setDisabled(false);
7304     },
7305     
7306     initEvents: function() 
7307     {
7308         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7309         
7310         this.iconEl.on('click', this.onClick, this);
7311     },
7312     
7313     onClick : function(e)
7314     {
7315         e.preventDefault();
7316         
7317         if(this.disabled){
7318             return;
7319         }
7320         
7321         if(this.fireEvent('click', this, e) === false){
7322             return;
7323         };
7324         
7325         this.parent().setActiveItem(this);
7326     },
7327     
7328     isActive: function () 
7329     {
7330         return this.active;
7331     },
7332     
7333     setActive : function(state)
7334     {
7335         if(this.active == state){
7336             return;
7337         }
7338         
7339         this.active = state;
7340         
7341         if (state) {
7342             this.el.addClass('active');
7343             return;
7344         }
7345         
7346         this.el.removeClass('active');
7347         
7348         return;
7349     },
7350     
7351     setDisabled : function(state)
7352     {
7353         if(this.disabled == state){
7354             return;
7355         }
7356         
7357         this.disabled = state;
7358         
7359         if (state) {
7360             this.el.addClass('disabled');
7361             return;
7362         }
7363         
7364         this.el.removeClass('disabled');
7365     },
7366     
7367     tooltipEl : function()
7368     {
7369         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7370     }
7371 });
7372  
7373
7374  /*
7375  * - LGPL
7376  *
7377  *  Breadcrumb Nav
7378  * 
7379  */
7380 Roo.namespace('Roo.bootstrap.breadcrumb');
7381
7382
7383 /**
7384  * @class Roo.bootstrap.breadcrumb.Nav
7385  * @extends Roo.bootstrap.Component
7386  * Bootstrap Breadcrumb Nav Class
7387  *  
7388  * @children Roo.bootstrap.breadcrumb.Item
7389  * 
7390  * @constructor
7391  * Create a new breadcrumb.Nav
7392  * @param {Object} config The config object
7393  */
7394
7395
7396 Roo.bootstrap.breadcrumb.Nav = function(config){
7397     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7398     
7399     
7400 };
7401
7402 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7403     
7404     getAutoCreate : function()
7405     {
7406
7407         var cfg = {
7408             tag: 'nav',
7409             cn : [
7410                 {
7411                     tag : 'ol',
7412                     cls : 'breadcrumb'
7413                 }
7414             ]
7415             
7416         };
7417           
7418         return cfg;
7419     },
7420     
7421     initEvents: function()
7422     {
7423         this.olEl = this.el.select('ol',true).first();    
7424     },
7425     getChildContainer : function()
7426     {
7427         return this.olEl;  
7428     }
7429     
7430 });
7431
7432  /*
7433  * - LGPL
7434  *
7435  *  Breadcrumb Item
7436  * 
7437  */
7438
7439
7440 /**
7441  * @class Roo.bootstrap.breadcrumb.Nav
7442  * @extends Roo.bootstrap.Component
7443  * @children Roo.bootstrap.Component
7444  * @parent Roo.bootstrap.breadcrumb.Nav
7445  * Bootstrap Breadcrumb Nav Class
7446  *  
7447  * 
7448  * @cfg {String} html the content of the link.
7449  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7450  * @cfg {Boolean} active is it active
7451
7452  * 
7453  * @constructor
7454  * Create a new breadcrumb.Nav
7455  * @param {Object} config The config object
7456  */
7457
7458 Roo.bootstrap.breadcrumb.Item = function(config){
7459     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7460     this.addEvents({
7461         // img events
7462         /**
7463          * @event click
7464          * The img click event for the img.
7465          * @param {Roo.EventObject} e
7466          */
7467         "click" : true
7468     });
7469     
7470 };
7471
7472 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7473     
7474     href: false,
7475     html : '',
7476     
7477     getAutoCreate : function()
7478     {
7479
7480         var cfg = {
7481             tag: 'li',
7482             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7483         };
7484         if (this.href !== false) {
7485             cfg.cn = [{
7486                 tag : 'a',
7487                 href : this.href,
7488                 html : this.html
7489             }];
7490         } else {
7491             cfg.html = this.html;
7492         }
7493         
7494         return cfg;
7495     },
7496     
7497     initEvents: function()
7498     {
7499         if (this.href) {
7500             this.el.select('a', true).first().on('click',this.onClick, this)
7501         }
7502         
7503     },
7504     onClick : function(e)
7505     {
7506         e.preventDefault();
7507         this.fireEvent('click',this,  e);
7508     }
7509     
7510 });
7511
7512  /*
7513  * - LGPL
7514  *
7515  * row
7516  * 
7517  */
7518
7519 /**
7520  * @class Roo.bootstrap.Row
7521  * @extends Roo.bootstrap.Component
7522  * @children Roo.bootstrap.Component
7523  * Bootstrap Row class (contains columns...)
7524  * 
7525  * @constructor
7526  * Create a new Row
7527  * @param {Object} config The config object
7528  */
7529
7530 Roo.bootstrap.Row = function(config){
7531     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7532 };
7533
7534 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7535     
7536     getAutoCreate : function(){
7537        return {
7538             cls: 'row clearfix'
7539        };
7540     }
7541     
7542     
7543 });
7544
7545  
7546
7547  /*
7548  * - LGPL
7549  *
7550  * pagination
7551  * 
7552  */
7553
7554 /**
7555  * @class Roo.bootstrap.Pagination
7556  * @extends Roo.bootstrap.Component
7557  * @children Roo.bootstrap.Pagination
7558  * Bootstrap Pagination class
7559  * 
7560  * @cfg {String} size (xs|sm|md|lg|xl)
7561  * @cfg {Boolean} inverse 
7562  * 
7563  * @constructor
7564  * Create a new Pagination
7565  * @param {Object} config The config object
7566  */
7567
7568 Roo.bootstrap.Pagination = function(config){
7569     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7570 };
7571
7572 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7573     
7574     cls: false,
7575     size: false,
7576     inverse: false,
7577     
7578     getAutoCreate : function(){
7579         var cfg = {
7580             tag: 'ul',
7581                 cls: 'pagination'
7582         };
7583         if (this.inverse) {
7584             cfg.cls += ' inverse';
7585         }
7586         if (this.html) {
7587             cfg.html=this.html;
7588         }
7589         if (this.cls) {
7590             cfg.cls += " " + this.cls;
7591         }
7592         return cfg;
7593     }
7594    
7595 });
7596
7597  
7598
7599  /*
7600  * - LGPL
7601  *
7602  * Pagination item
7603  * 
7604  */
7605
7606
7607 /**
7608  * @class Roo.bootstrap.PaginationItem
7609  * @extends Roo.bootstrap.Component
7610  * Bootstrap PaginationItem class
7611  * @cfg {String} html text
7612  * @cfg {String} href the link
7613  * @cfg {Boolean} preventDefault (true | false) default true
7614  * @cfg {Boolean} active (true | false) default false
7615  * @cfg {Boolean} disabled default false
7616  * 
7617  * 
7618  * @constructor
7619  * Create a new PaginationItem
7620  * @param {Object} config The config object
7621  */
7622
7623
7624 Roo.bootstrap.PaginationItem = function(config){
7625     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7626     this.addEvents({
7627         // raw events
7628         /**
7629          * @event click
7630          * The raw click event for the entire grid.
7631          * @param {Roo.EventObject} e
7632          */
7633         "click" : true
7634     });
7635 };
7636
7637 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7638     
7639     href : false,
7640     html : false,
7641     preventDefault: true,
7642     active : false,
7643     cls : false,
7644     disabled: false,
7645     
7646     getAutoCreate : function(){
7647         var cfg= {
7648             tag: 'li',
7649             cn: [
7650                 {
7651                     tag : 'a',
7652                     href : this.href ? this.href : '#',
7653                     html : this.html ? this.html : ''
7654                 }
7655             ]
7656         };
7657         
7658         if(this.cls){
7659             cfg.cls = this.cls;
7660         }
7661         
7662         if(this.disabled){
7663             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7664         }
7665         
7666         if(this.active){
7667             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7668         }
7669         
7670         return cfg;
7671     },
7672     
7673     initEvents: function() {
7674         
7675         this.el.on('click', this.onClick, this);
7676         
7677     },
7678     onClick : function(e)
7679     {
7680         Roo.log('PaginationItem on click ');
7681         if(this.preventDefault){
7682             e.preventDefault();
7683         }
7684         
7685         if(this.disabled){
7686             return;
7687         }
7688         
7689         this.fireEvent('click', this, e);
7690     }
7691    
7692 });
7693
7694  
7695
7696  /*
7697  * - LGPL
7698  *
7699  * slider
7700  * 
7701  */
7702
7703
7704 /**
7705  * @class Roo.bootstrap.Slider
7706  * @extends Roo.bootstrap.Component
7707  * Bootstrap Slider class
7708  *    
7709  * @constructor
7710  * Create a new Slider
7711  * @param {Object} config The config object
7712  */
7713
7714 Roo.bootstrap.Slider = function(config){
7715     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7716 };
7717
7718 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7719     
7720     getAutoCreate : function(){
7721         
7722         var cfg = {
7723             tag: 'div',
7724             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7725             cn: [
7726                 {
7727                     tag: 'a',
7728                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7729                 }
7730             ]
7731         };
7732         
7733         return cfg;
7734     }
7735    
7736 });
7737
7738  /*
7739  * Based on:
7740  * Ext JS Library 1.1.1
7741  * Copyright(c) 2006-2007, Ext JS, LLC.
7742  *
7743  * Originally Released Under LGPL - original licence link has changed is not relivant.
7744  *
7745  * Fork - LGPL
7746  * <script type="text/javascript">
7747  */
7748  /**
7749  * @extends Roo.dd.DDProxy
7750  * @class Roo.grid.SplitDragZone
7751  * Support for Column Header resizing
7752  * @constructor
7753  * @param {Object} config
7754  */
7755 // private
7756 // This is a support class used internally by the Grid components
7757 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7758     this.grid = grid;
7759     this.view = grid.getView();
7760     this.proxy = this.view.resizeProxy;
7761     Roo.grid.SplitDragZone.superclass.constructor.call(
7762         this,
7763         hd, // ID
7764         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7765         {  // CONFIG
7766             dragElId : Roo.id(this.proxy.dom),
7767             resizeFrame:false
7768         }
7769     );
7770     
7771     this.setHandleElId(Roo.id(hd));
7772     if (hd2 !== false) {
7773         this.setOuterHandleElId(Roo.id(hd2));
7774     }
7775     
7776     this.scroll = false;
7777 };
7778 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7779     fly: Roo.Element.fly,
7780
7781     b4StartDrag : function(x, y){
7782         this.view.headersDisabled = true;
7783         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7784                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7785         );
7786         this.proxy.setHeight(h);
7787         
7788         // for old system colWidth really stored the actual width?
7789         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7790         // which in reality did not work.. - it worked only for fixed sizes
7791         // for resizable we need to use actual sizes.
7792         var w = this.cm.getColumnWidth(this.cellIndex);
7793         if (!this.view.mainWrap) {
7794             // bootstrap.
7795             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7796         }
7797         
7798         
7799         
7800         // this was w-this.grid.minColumnWidth;
7801         // doesnt really make sense? - w = thie curren width or the rendered one?
7802         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7803         this.resetConstraints();
7804         this.setXConstraint(minw, 1000);
7805         this.setYConstraint(0, 0);
7806         this.minX = x - minw;
7807         this.maxX = x + 1000;
7808         this.startPos = x;
7809         if (!this.view.mainWrap) { // this is Bootstrap code..
7810             this.getDragEl().style.display='block';
7811         }
7812         
7813         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7814     },
7815
7816
7817     handleMouseDown : function(e){
7818         ev = Roo.EventObject.setEvent(e);
7819         var t = this.fly(ev.getTarget());
7820         if(t.hasClass("x-grid-split")){
7821             this.cellIndex = this.view.getCellIndex(t.dom);
7822             this.split = t.dom;
7823             this.cm = this.grid.colModel;
7824             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7825                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7826             }
7827         }
7828     },
7829
7830     endDrag : function(e){
7831         this.view.headersDisabled = false;
7832         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7833         var diff = endX - this.startPos;
7834         // 
7835         var w = this.cm.getColumnWidth(this.cellIndex);
7836         if (!this.view.mainWrap) {
7837             w = 0;
7838         }
7839         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7840     },
7841
7842     autoOffset : function(){
7843         this.setDelta(0,0);
7844     }
7845 });/*
7846  * Based on:
7847  * Ext JS Library 1.1.1
7848  * Copyright(c) 2006-2007, Ext JS, LLC.
7849  *
7850  * Originally Released Under LGPL - original licence link has changed is not relivant.
7851  *
7852  * Fork - LGPL
7853  * <script type="text/javascript">
7854  */
7855
7856 /**
7857  * @class Roo.grid.AbstractSelectionModel
7858  * @extends Roo.util.Observable
7859  * @abstract
7860  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7861  * implemented by descendant classes.  This class should not be directly instantiated.
7862  * @constructor
7863  */
7864 Roo.grid.AbstractSelectionModel = function(){
7865     this.locked = false;
7866     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7867 };
7868
7869 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7870     /** @ignore Called by the grid automatically. Do not call directly. */
7871     init : function(grid){
7872         this.grid = grid;
7873         this.initEvents();
7874     },
7875
7876     /**
7877      * Locks the selections.
7878      */
7879     lock : function(){
7880         this.locked = true;
7881     },
7882
7883     /**
7884      * Unlocks the selections.
7885      */
7886     unlock : function(){
7887         this.locked = false;
7888     },
7889
7890     /**
7891      * Returns true if the selections are locked.
7892      * @return {Boolean}
7893      */
7894     isLocked : function(){
7895         return this.locked;
7896     }
7897 });/*
7898  * Based on:
7899  * Ext JS Library 1.1.1
7900  * Copyright(c) 2006-2007, Ext JS, LLC.
7901  *
7902  * Originally Released Under LGPL - original licence link has changed is not relivant.
7903  *
7904  * Fork - LGPL
7905  * <script type="text/javascript">
7906  */
7907 /**
7908  * @extends Roo.grid.AbstractSelectionModel
7909  * @class Roo.grid.RowSelectionModel
7910  * The default SelectionModel used by {@link Roo.grid.Grid}.
7911  * It supports multiple selections and keyboard selection/navigation. 
7912  * @constructor
7913  * @param {Object} config
7914  */
7915 Roo.grid.RowSelectionModel = function(config){
7916     Roo.apply(this, config);
7917     this.selections = new Roo.util.MixedCollection(false, function(o){
7918         return o.id;
7919     });
7920
7921     this.last = false;
7922     this.lastActive = false;
7923
7924     this.addEvents({
7925         /**
7926         * @event selectionchange
7927         * Fires when the selection changes
7928         * @param {SelectionModel} this
7929         */
7930        "selectionchange" : true,
7931        /**
7932         * @event afterselectionchange
7933         * Fires after the selection changes (eg. by key press or clicking)
7934         * @param {SelectionModel} this
7935         */
7936        "afterselectionchange" : true,
7937        /**
7938         * @event beforerowselect
7939         * Fires when a row is selected being selected, return false to cancel.
7940         * @param {SelectionModel} this
7941         * @param {Number} rowIndex The selected index
7942         * @param {Boolean} keepExisting False if other selections will be cleared
7943         */
7944        "beforerowselect" : true,
7945        /**
7946         * @event rowselect
7947         * Fires when a row is selected.
7948         * @param {SelectionModel} this
7949         * @param {Number} rowIndex The selected index
7950         * @param {Roo.data.Record} r The record
7951         */
7952        "rowselect" : true,
7953        /**
7954         * @event rowdeselect
7955         * Fires when a row is deselected.
7956         * @param {SelectionModel} this
7957         * @param {Number} rowIndex The selected index
7958         */
7959         "rowdeselect" : true
7960     });
7961     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7962     this.locked = false;
7963 };
7964
7965 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7966     /**
7967      * @cfg {Boolean} singleSelect
7968      * True to allow selection of only one row at a time (defaults to false)
7969      */
7970     singleSelect : false,
7971
7972     // private
7973     initEvents : function(){
7974
7975         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7976             this.grid.on("mousedown", this.handleMouseDown, this);
7977         }else{ // allow click to work like normal
7978             this.grid.on("rowclick", this.handleDragableRowClick, this);
7979         }
7980         // bootstrap does not have a view..
7981         var view = this.grid.view ? this.grid.view : this.grid;
7982         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7983             "up" : function(e){
7984                 if(!e.shiftKey){
7985                     this.selectPrevious(e.shiftKey);
7986                 }else if(this.last !== false && this.lastActive !== false){
7987                     var last = this.last;
7988                     this.selectRange(this.last,  this.lastActive-1);
7989                     view.focusRow(this.lastActive);
7990                     if(last !== false){
7991                         this.last = last;
7992                     }
7993                 }else{
7994                     this.selectFirstRow();
7995                 }
7996                 this.fireEvent("afterselectionchange", this);
7997             },
7998             "down" : function(e){
7999                 if(!e.shiftKey){
8000                     this.selectNext(e.shiftKey);
8001                 }else if(this.last !== false && this.lastActive !== false){
8002                     var last = this.last;
8003                     this.selectRange(this.last,  this.lastActive+1);
8004                     view.focusRow(this.lastActive);
8005                     if(last !== false){
8006                         this.last = last;
8007                     }
8008                 }else{
8009                     this.selectFirstRow();
8010                 }
8011                 this.fireEvent("afterselectionchange", this);
8012             },
8013             scope: this
8014         });
8015
8016          
8017         view.on("refresh", this.onRefresh, this);
8018         view.on("rowupdated", this.onRowUpdated, this);
8019         view.on("rowremoved", this.onRemove, this);
8020     },
8021
8022     // private
8023     onRefresh : function(){
8024         var ds = this.grid.ds, i, v = this.grid.view;
8025         var s = this.selections;
8026         s.each(function(r){
8027             if((i = ds.indexOfId(r.id)) != -1){
8028                 v.onRowSelect(i);
8029                 s.add(ds.getAt(i)); // updating the selection relate data
8030             }else{
8031                 s.remove(r);
8032             }
8033         });
8034     },
8035
8036     // private
8037     onRemove : function(v, index, r){
8038         this.selections.remove(r);
8039     },
8040
8041     // private
8042     onRowUpdated : function(v, index, r){
8043         if(this.isSelected(r)){
8044             v.onRowSelect(index);
8045         }
8046     },
8047
8048     /**
8049      * Select records.
8050      * @param {Array} records The records to select
8051      * @param {Boolean} keepExisting (optional) True to keep existing selections
8052      */
8053     selectRecords : function(records, keepExisting){
8054         if(!keepExisting){
8055             this.clearSelections();
8056         }
8057         var ds = this.grid.ds;
8058         for(var i = 0, len = records.length; i < len; i++){
8059             this.selectRow(ds.indexOf(records[i]), true);
8060         }
8061     },
8062
8063     /**
8064      * Gets the number of selected rows.
8065      * @return {Number}
8066      */
8067     getCount : function(){
8068         return this.selections.length;
8069     },
8070
8071     /**
8072      * Selects the first row in the grid.
8073      */
8074     selectFirstRow : function(){
8075         this.selectRow(0);
8076     },
8077
8078     /**
8079      * Select the last row.
8080      * @param {Boolean} keepExisting (optional) True to keep existing selections
8081      */
8082     selectLastRow : function(keepExisting){
8083         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8084     },
8085
8086     /**
8087      * Selects the row immediately following the last selected row.
8088      * @param {Boolean} keepExisting (optional) True to keep existing selections
8089      */
8090     selectNext : function(keepExisting){
8091         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8092             this.selectRow(this.last+1, keepExisting);
8093             var view = this.grid.view ? this.grid.view : this.grid;
8094             view.focusRow(this.last);
8095         }
8096     },
8097
8098     /**
8099      * Selects the row that precedes the last selected row.
8100      * @param {Boolean} keepExisting (optional) True to keep existing selections
8101      */
8102     selectPrevious : function(keepExisting){
8103         if(this.last){
8104             this.selectRow(this.last-1, keepExisting);
8105             var view = this.grid.view ? this.grid.view : this.grid;
8106             view.focusRow(this.last);
8107         }
8108     },
8109
8110     /**
8111      * Returns the selected records
8112      * @return {Array} Array of selected records
8113      */
8114     getSelections : function(){
8115         return [].concat(this.selections.items);
8116     },
8117
8118     /**
8119      * Returns the first selected record.
8120      * @return {Record}
8121      */
8122     getSelected : function(){
8123         return this.selections.itemAt(0);
8124     },
8125
8126
8127     /**
8128      * Clears all selections.
8129      */
8130     clearSelections : function(fast){
8131         if(this.locked) {
8132             return;
8133         }
8134         if(fast !== true){
8135             var ds = this.grid.ds;
8136             var s = this.selections;
8137             s.each(function(r){
8138                 this.deselectRow(ds.indexOfId(r.id));
8139             }, this);
8140             s.clear();
8141         }else{
8142             this.selections.clear();
8143         }
8144         this.last = false;
8145     },
8146
8147
8148     /**
8149      * Selects all rows.
8150      */
8151     selectAll : function(){
8152         if(this.locked) {
8153             return;
8154         }
8155         this.selections.clear();
8156         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8157             this.selectRow(i, true);
8158         }
8159     },
8160
8161     /**
8162      * Returns True if there is a selection.
8163      * @return {Boolean}
8164      */
8165     hasSelection : function(){
8166         return this.selections.length > 0;
8167     },
8168
8169     /**
8170      * Returns True if the specified row is selected.
8171      * @param {Number/Record} record The record or index of the record to check
8172      * @return {Boolean}
8173      */
8174     isSelected : function(index){
8175         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8176         return (r && this.selections.key(r.id) ? true : false);
8177     },
8178
8179     /**
8180      * Returns True if the specified record id is selected.
8181      * @param {String} id The id of record to check
8182      * @return {Boolean}
8183      */
8184     isIdSelected : function(id){
8185         return (this.selections.key(id) ? true : false);
8186     },
8187
8188     // private
8189     handleMouseDown : function(e, t)
8190     {
8191         var view = this.grid.view ? this.grid.view : this.grid;
8192         var rowIndex;
8193         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8194             return;
8195         };
8196         if(e.shiftKey && this.last !== false){
8197             var last = this.last;
8198             this.selectRange(last, rowIndex, e.ctrlKey);
8199             this.last = last; // reset the last
8200             view.focusRow(rowIndex);
8201         }else{
8202             var isSelected = this.isSelected(rowIndex);
8203             if(e.button !== 0 && isSelected){
8204                 view.focusRow(rowIndex);
8205             }else if(e.ctrlKey && isSelected){
8206                 this.deselectRow(rowIndex);
8207             }else if(!isSelected){
8208                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8209                 view.focusRow(rowIndex);
8210             }
8211         }
8212         this.fireEvent("afterselectionchange", this);
8213     },
8214     // private
8215     handleDragableRowClick :  function(grid, rowIndex, e) 
8216     {
8217         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8218             this.selectRow(rowIndex, false);
8219             var view = this.grid.view ? this.grid.view : this.grid;
8220             view.focusRow(rowIndex);
8221              this.fireEvent("afterselectionchange", this);
8222         }
8223     },
8224     
8225     /**
8226      * Selects multiple rows.
8227      * @param {Array} rows Array of the indexes of the row to select
8228      * @param {Boolean} keepExisting (optional) True to keep existing selections
8229      */
8230     selectRows : function(rows, keepExisting){
8231         if(!keepExisting){
8232             this.clearSelections();
8233         }
8234         for(var i = 0, len = rows.length; i < len; i++){
8235             this.selectRow(rows[i], true);
8236         }
8237     },
8238
8239     /**
8240      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8241      * @param {Number} startRow The index of the first row in the range
8242      * @param {Number} endRow The index of the last row in the range
8243      * @param {Boolean} keepExisting (optional) True to retain existing selections
8244      */
8245     selectRange : function(startRow, endRow, keepExisting){
8246         if(this.locked) {
8247             return;
8248         }
8249         if(!keepExisting){
8250             this.clearSelections();
8251         }
8252         if(startRow <= endRow){
8253             for(var i = startRow; i <= endRow; i++){
8254                 this.selectRow(i, true);
8255             }
8256         }else{
8257             for(var i = startRow; i >= endRow; i--){
8258                 this.selectRow(i, true);
8259             }
8260         }
8261     },
8262
8263     /**
8264      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8265      * @param {Number} startRow The index of the first row in the range
8266      * @param {Number} endRow The index of the last row in the range
8267      */
8268     deselectRange : function(startRow, endRow, preventViewNotify){
8269         if(this.locked) {
8270             return;
8271         }
8272         for(var i = startRow; i <= endRow; i++){
8273             this.deselectRow(i, preventViewNotify);
8274         }
8275     },
8276
8277     /**
8278      * Selects a row.
8279      * @param {Number} row The index of the row to select
8280      * @param {Boolean} keepExisting (optional) True to keep existing selections
8281      */
8282     selectRow : function(index, keepExisting, preventViewNotify){
8283         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8284             return;
8285         }
8286         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8287             if(!keepExisting || this.singleSelect){
8288                 this.clearSelections();
8289             }
8290             var r = this.grid.ds.getAt(index);
8291             this.selections.add(r);
8292             this.last = this.lastActive = index;
8293             if(!preventViewNotify){
8294                 var view = this.grid.view ? this.grid.view : this.grid;
8295                 view.onRowSelect(index);
8296             }
8297             this.fireEvent("rowselect", this, index, r);
8298             this.fireEvent("selectionchange", this);
8299         }
8300     },
8301
8302     /**
8303      * Deselects a row.
8304      * @param {Number} row The index of the row to deselect
8305      */
8306     deselectRow : function(index, preventViewNotify){
8307         if(this.locked) {
8308             return;
8309         }
8310         if(this.last == index){
8311             this.last = false;
8312         }
8313         if(this.lastActive == index){
8314             this.lastActive = false;
8315         }
8316         var r = this.grid.ds.getAt(index);
8317         this.selections.remove(r);
8318         if(!preventViewNotify){
8319             var view = this.grid.view ? this.grid.view : this.grid;
8320             view.onRowDeselect(index);
8321         }
8322         this.fireEvent("rowdeselect", this, index);
8323         this.fireEvent("selectionchange", this);
8324     },
8325
8326     // private
8327     restoreLast : function(){
8328         if(this._last){
8329             this.last = this._last;
8330         }
8331     },
8332
8333     // private
8334     acceptsNav : function(row, col, cm){
8335         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8336     },
8337
8338     // private
8339     onEditorKey : function(field, e){
8340         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8341         if(k == e.TAB){
8342             e.stopEvent();
8343             ed.completeEdit();
8344             if(e.shiftKey){
8345                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8346             }else{
8347                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8348             }
8349         }else if(k == e.ENTER && !e.ctrlKey){
8350             e.stopEvent();
8351             ed.completeEdit();
8352             if(e.shiftKey){
8353                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8354             }else{
8355                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8356             }
8357         }else if(k == e.ESC){
8358             ed.cancelEdit();
8359         }
8360         if(newCell){
8361             g.startEditing(newCell[0], newCell[1]);
8362         }
8363     }
8364 });/*
8365  * Based on:
8366  * Ext JS Library 1.1.1
8367  * Copyright(c) 2006-2007, Ext JS, LLC.
8368  *
8369  * Originally Released Under LGPL - original licence link has changed is not relivant.
8370  *
8371  * Fork - LGPL
8372  * <script type="text/javascript">
8373  */
8374  
8375
8376 /**
8377  * @class Roo.grid.ColumnModel
8378  * @extends Roo.util.Observable
8379  * This is the default implementation of a ColumnModel used by the Grid. It defines
8380  * the columns in the grid.
8381  * <br>Usage:<br>
8382  <pre><code>
8383  var colModel = new Roo.grid.ColumnModel([
8384         {header: "Ticker", width: 60, sortable: true, locked: true},
8385         {header: "Company Name", width: 150, sortable: true},
8386         {header: "Market Cap.", width: 100, sortable: true},
8387         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8388         {header: "Employees", width: 100, sortable: true, resizable: false}
8389  ]);
8390  </code></pre>
8391  * <p>
8392  
8393  * The config options listed for this class are options which may appear in each
8394  * individual column definition.
8395  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8396  * @constructor
8397  * @param {Object} config An Array of column config objects. See this class's
8398  * config objects for details.
8399 */
8400 Roo.grid.ColumnModel = function(config){
8401         /**
8402      * The config passed into the constructor
8403      */
8404     this.config = []; //config;
8405     this.lookup = {};
8406
8407     // if no id, create one
8408     // if the column does not have a dataIndex mapping,
8409     // map it to the order it is in the config
8410     for(var i = 0, len = config.length; i < len; i++){
8411         this.addColumn(config[i]);
8412         
8413     }
8414
8415     /**
8416      * The width of columns which have no width specified (defaults to 100)
8417      * @type Number
8418      */
8419     this.defaultWidth = 100;
8420
8421     /**
8422      * Default sortable of columns which have no sortable specified (defaults to false)
8423      * @type Boolean
8424      */
8425     this.defaultSortable = false;
8426
8427     this.addEvents({
8428         /**
8429              * @event widthchange
8430              * Fires when the width of a column changes.
8431              * @param {ColumnModel} this
8432              * @param {Number} columnIndex The column index
8433              * @param {Number} newWidth The new width
8434              */
8435             "widthchange": true,
8436         /**
8437              * @event headerchange
8438              * Fires when the text of a header changes.
8439              * @param {ColumnModel} this
8440              * @param {Number} columnIndex The column index
8441              * @param {Number} newText The new header text
8442              */
8443             "headerchange": true,
8444         /**
8445              * @event hiddenchange
8446              * Fires when a column is hidden or "unhidden".
8447              * @param {ColumnModel} this
8448              * @param {Number} columnIndex The column index
8449              * @param {Boolean} hidden true if hidden, false otherwise
8450              */
8451             "hiddenchange": true,
8452             /**
8453          * @event columnmoved
8454          * Fires when a column is moved.
8455          * @param {ColumnModel} this
8456          * @param {Number} oldIndex
8457          * @param {Number} newIndex
8458          */
8459         "columnmoved" : true,
8460         /**
8461          * @event columlockchange
8462          * Fires when a column's locked state is changed
8463          * @param {ColumnModel} this
8464          * @param {Number} colIndex
8465          * @param {Boolean} locked true if locked
8466          */
8467         "columnlockchange" : true
8468     });
8469     Roo.grid.ColumnModel.superclass.constructor.call(this);
8470 };
8471 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8472     /**
8473      * @cfg {String} header [required] The header text to display in the Grid view.
8474      */
8475         /**
8476      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8477      */
8478         /**
8479      * @cfg {String} smHeader Header at Bootsrap Small width
8480      */
8481         /**
8482      * @cfg {String} mdHeader Header at Bootsrap Medium width
8483      */
8484         /**
8485      * @cfg {String} lgHeader Header at Bootsrap Large width
8486      */
8487         /**
8488      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8489      */
8490     /**
8491      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8492      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8493      * specified, the column's index is used as an index into the Record's data Array.
8494      */
8495     /**
8496      * @cfg {Number} width  The initial width in pixels of the column. Using this
8497      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8498      */
8499     /**
8500      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8501      * Defaults to the value of the {@link #defaultSortable} property.
8502      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8503      */
8504     /**
8505      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8506      */
8507     /**
8508      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8509      */
8510     /**
8511      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8512      */
8513     /**
8514      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8515      */
8516     /**
8517      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8518      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8519      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8520      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8521      */
8522        /**
8523      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8524      */
8525     /**
8526      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8527      */
8528     /**
8529      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8530      */
8531     /**
8532      * @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)
8533      */
8534     /**
8535      * @cfg {String} tooltip mouse over tooltip text
8536      */
8537     /**
8538      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8539      */
8540     /**
8541      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8542      */
8543     /**
8544      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8545      */
8546     /**
8547      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8548      */
8549         /**
8550      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8551      */
8552     /**
8553      * Returns the id of the column at the specified index.
8554      * @param {Number} index The column index
8555      * @return {String} the id
8556      */
8557     getColumnId : function(index){
8558         return this.config[index].id;
8559     },
8560
8561     /**
8562      * Returns the column for a specified id.
8563      * @param {String} id The column id
8564      * @return {Object} the column
8565      */
8566     getColumnById : function(id){
8567         return this.lookup[id];
8568     },
8569
8570     
8571     /**
8572      * Returns the column Object for a specified dataIndex.
8573      * @param {String} dataIndex The column dataIndex
8574      * @return {Object|Boolean} the column or false if not found
8575      */
8576     getColumnByDataIndex: function(dataIndex){
8577         var index = this.findColumnIndex(dataIndex);
8578         return index > -1 ? this.config[index] : false;
8579     },
8580     
8581     /**
8582      * Returns the index for a specified column id.
8583      * @param {String} id The column id
8584      * @return {Number} the index, or -1 if not found
8585      */
8586     getIndexById : function(id){
8587         for(var i = 0, len = this.config.length; i < len; i++){
8588             if(this.config[i].id == id){
8589                 return i;
8590             }
8591         }
8592         return -1;
8593     },
8594     
8595     /**
8596      * Returns the index for a specified column dataIndex.
8597      * @param {String} dataIndex The column dataIndex
8598      * @return {Number} the index, or -1 if not found
8599      */
8600     
8601     findColumnIndex : function(dataIndex){
8602         for(var i = 0, len = this.config.length; i < len; i++){
8603             if(this.config[i].dataIndex == dataIndex){
8604                 return i;
8605             }
8606         }
8607         return -1;
8608     },
8609     
8610     
8611     moveColumn : function(oldIndex, newIndex){
8612         var c = this.config[oldIndex];
8613         this.config.splice(oldIndex, 1);
8614         this.config.splice(newIndex, 0, c);
8615         this.dataMap = null;
8616         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8617     },
8618
8619     isLocked : function(colIndex){
8620         return this.config[colIndex].locked === true;
8621     },
8622
8623     setLocked : function(colIndex, value, suppressEvent){
8624         if(this.isLocked(colIndex) == value){
8625             return;
8626         }
8627         this.config[colIndex].locked = value;
8628         if(!suppressEvent){
8629             this.fireEvent("columnlockchange", this, colIndex, value);
8630         }
8631     },
8632
8633     getTotalLockedWidth : function(){
8634         var totalWidth = 0;
8635         for(var i = 0; i < this.config.length; i++){
8636             if(this.isLocked(i) && !this.isHidden(i)){
8637                 this.totalWidth += this.getColumnWidth(i);
8638             }
8639         }
8640         return totalWidth;
8641     },
8642
8643     getLockedCount : function(){
8644         for(var i = 0, len = this.config.length; i < len; i++){
8645             if(!this.isLocked(i)){
8646                 return i;
8647             }
8648         }
8649         
8650         return this.config.length;
8651     },
8652
8653     /**
8654      * Returns the number of columns.
8655      * @return {Number}
8656      */
8657     getColumnCount : function(visibleOnly){
8658         if(visibleOnly === true){
8659             var c = 0;
8660             for(var i = 0, len = this.config.length; i < len; i++){
8661                 if(!this.isHidden(i)){
8662                     c++;
8663                 }
8664             }
8665             return c;
8666         }
8667         return this.config.length;
8668     },
8669
8670     /**
8671      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8672      * @param {Function} fn
8673      * @param {Object} scope (optional)
8674      * @return {Array} result
8675      */
8676     getColumnsBy : function(fn, scope){
8677         var r = [];
8678         for(var i = 0, len = this.config.length; i < len; i++){
8679             var c = this.config[i];
8680             if(fn.call(scope||this, c, i) === true){
8681                 r[r.length] = c;
8682             }
8683         }
8684         return r;
8685     },
8686
8687     /**
8688      * Returns true if the specified column is sortable.
8689      * @param {Number} col The column index
8690      * @return {Boolean}
8691      */
8692     isSortable : function(col){
8693         if(typeof this.config[col].sortable == "undefined"){
8694             return this.defaultSortable;
8695         }
8696         return this.config[col].sortable;
8697     },
8698
8699     /**
8700      * Returns the rendering (formatting) function defined for the column.
8701      * @param {Number} col The column index.
8702      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8703      */
8704     getRenderer : function(col){
8705         if(!this.config[col].renderer){
8706             return Roo.grid.ColumnModel.defaultRenderer;
8707         }
8708         return this.config[col].renderer;
8709     },
8710
8711     /**
8712      * Sets the rendering (formatting) function for a column.
8713      * @param {Number} col The column index
8714      * @param {Function} fn The function to use to process the cell's raw data
8715      * to return HTML markup for the grid view. The render function is called with
8716      * the following parameters:<ul>
8717      * <li>Data value.</li>
8718      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8719      * <li>css A CSS style string to apply to the table cell.</li>
8720      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8721      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8722      * <li>Row index</li>
8723      * <li>Column index</li>
8724      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8725      */
8726     setRenderer : function(col, fn){
8727         this.config[col].renderer = fn;
8728     },
8729
8730     /**
8731      * Returns the width for the specified column.
8732      * @param {Number} col The column index
8733      * @param (optional) {String} gridSize bootstrap width size.
8734      * @return {Number}
8735      */
8736     getColumnWidth : function(col, gridSize)
8737         {
8738                 var cfg = this.config[col];
8739                 
8740                 if (typeof(gridSize) == 'undefined') {
8741                         return cfg.width * 1 || this.defaultWidth;
8742                 }
8743                 if (gridSize === false) { // if we set it..
8744                         return cfg.width || false;
8745                 }
8746                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8747                 
8748                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8749                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8750                                 continue;
8751                         }
8752                         return cfg[ sizes[i] ];
8753                 }
8754                 return 1;
8755                 
8756     },
8757
8758     /**
8759      * Sets the width for a column.
8760      * @param {Number} col The column index
8761      * @param {Number} width The new width
8762      */
8763     setColumnWidth : function(col, width, suppressEvent){
8764         this.config[col].width = width;
8765         this.totalWidth = null;
8766         if(!suppressEvent){
8767              this.fireEvent("widthchange", this, col, width);
8768         }
8769     },
8770
8771     /**
8772      * Returns the total width of all columns.
8773      * @param {Boolean} includeHidden True to include hidden column widths
8774      * @return {Number}
8775      */
8776     getTotalWidth : function(includeHidden){
8777         if(!this.totalWidth){
8778             this.totalWidth = 0;
8779             for(var i = 0, len = this.config.length; i < len; i++){
8780                 if(includeHidden || !this.isHidden(i)){
8781                     this.totalWidth += this.getColumnWidth(i);
8782                 }
8783             }
8784         }
8785         return this.totalWidth;
8786     },
8787
8788     /**
8789      * Returns the header for the specified column.
8790      * @param {Number} col The column index
8791      * @return {String}
8792      */
8793     getColumnHeader : function(col){
8794         return this.config[col].header;
8795     },
8796
8797     /**
8798      * Sets the header for a column.
8799      * @param {Number} col The column index
8800      * @param {String} header The new header
8801      */
8802     setColumnHeader : function(col, header){
8803         this.config[col].header = header;
8804         this.fireEvent("headerchange", this, col, header);
8805     },
8806
8807     /**
8808      * Returns the tooltip for the specified column.
8809      * @param {Number} col The column index
8810      * @return {String}
8811      */
8812     getColumnTooltip : function(col){
8813             return this.config[col].tooltip;
8814     },
8815     /**
8816      * Sets the tooltip for a column.
8817      * @param {Number} col The column index
8818      * @param {String} tooltip The new tooltip
8819      */
8820     setColumnTooltip : function(col, tooltip){
8821             this.config[col].tooltip = tooltip;
8822     },
8823
8824     /**
8825      * Returns the dataIndex for the specified column.
8826      * @param {Number} col The column index
8827      * @return {Number}
8828      */
8829     getDataIndex : function(col){
8830         return this.config[col].dataIndex;
8831     },
8832
8833     /**
8834      * Sets the dataIndex for a column.
8835      * @param {Number} col The column index
8836      * @param {Number} dataIndex The new dataIndex
8837      */
8838     setDataIndex : function(col, dataIndex){
8839         this.config[col].dataIndex = dataIndex;
8840     },
8841
8842     
8843     
8844     /**
8845      * Returns true if the cell is editable.
8846      * @param {Number} colIndex The column index
8847      * @param {Number} rowIndex The row index - this is nto actually used..?
8848      * @return {Boolean}
8849      */
8850     isCellEditable : function(colIndex, rowIndex){
8851         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8852     },
8853
8854     /**
8855      * Returns the editor defined for the cell/column.
8856      * return false or null to disable editing.
8857      * @param {Number} colIndex The column index
8858      * @param {Number} rowIndex The row index
8859      * @return {Object}
8860      */
8861     getCellEditor : function(colIndex, rowIndex){
8862         return this.config[colIndex].editor;
8863     },
8864
8865     /**
8866      * Sets if a column is editable.
8867      * @param {Number} col The column index
8868      * @param {Boolean} editable True if the column is editable
8869      */
8870     setEditable : function(col, editable){
8871         this.config[col].editable = editable;
8872     },
8873
8874
8875     /**
8876      * Returns true if the column is hidden.
8877      * @param {Number} colIndex The column index
8878      * @return {Boolean}
8879      */
8880     isHidden : function(colIndex){
8881         return this.config[colIndex].hidden;
8882     },
8883
8884
8885     /**
8886      * Returns true if the column width cannot be changed
8887      */
8888     isFixed : function(colIndex){
8889         return this.config[colIndex].fixed;
8890     },
8891
8892     /**
8893      * Returns true if the column can be resized
8894      * @return {Boolean}
8895      */
8896     isResizable : function(colIndex){
8897         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8898     },
8899     /**
8900      * Sets if a column is hidden.
8901      * @param {Number} colIndex The column index
8902      * @param {Boolean} hidden True if the column is hidden
8903      */
8904     setHidden : function(colIndex, hidden){
8905         this.config[colIndex].hidden = hidden;
8906         this.totalWidth = null;
8907         this.fireEvent("hiddenchange", this, colIndex, hidden);
8908     },
8909
8910     /**
8911      * Sets the editor for a column.
8912      * @param {Number} col The column index
8913      * @param {Object} editor The editor object
8914      */
8915     setEditor : function(col, editor){
8916         this.config[col].editor = editor;
8917     },
8918     /**
8919      * Add a column (experimental...) - defaults to adding to the end..
8920      * @param {Object} config 
8921     */
8922     addColumn : function(c)
8923     {
8924     
8925         var i = this.config.length;
8926         this.config[i] = c;
8927         
8928         if(typeof c.dataIndex == "undefined"){
8929             c.dataIndex = i;
8930         }
8931         if(typeof c.renderer == "string"){
8932             c.renderer = Roo.util.Format[c.renderer];
8933         }
8934         if(typeof c.id == "undefined"){
8935             c.id = Roo.id();
8936         }
8937         if(c.editor && c.editor.xtype){
8938             c.editor  = Roo.factory(c.editor, Roo.grid);
8939         }
8940         if(c.editor && c.editor.isFormField){
8941             c.editor = new Roo.grid.GridEditor(c.editor);
8942         }
8943         this.lookup[c.id] = c;
8944     }
8945     
8946 });
8947
8948 Roo.grid.ColumnModel.defaultRenderer = function(value)
8949 {
8950     if(typeof value == "object") {
8951         return value;
8952     }
8953         if(typeof value == "string" && value.length < 1){
8954             return "&#160;";
8955         }
8956     
8957         return String.format("{0}", value);
8958 };
8959
8960 // Alias for backwards compatibility
8961 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8962 /*
8963  * Based on:
8964  * Ext JS Library 1.1.1
8965  * Copyright(c) 2006-2007, Ext JS, LLC.
8966  *
8967  * Originally Released Under LGPL - original licence link has changed is not relivant.
8968  *
8969  * Fork - LGPL
8970  * <script type="text/javascript">
8971  */
8972  
8973 /**
8974  * @class Roo.LoadMask
8975  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8976  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8977  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8978  * element's UpdateManager load indicator and will be destroyed after the initial load.
8979  * @constructor
8980  * Create a new LoadMask
8981  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8982  * @param {Object} config The config object
8983  */
8984 Roo.LoadMask = function(el, config){
8985     this.el = Roo.get(el);
8986     Roo.apply(this, config);
8987     if(this.store){
8988         this.store.on('beforeload', this.onBeforeLoad, this);
8989         this.store.on('load', this.onLoad, this);
8990         this.store.on('loadexception', this.onLoadException, this);
8991         this.removeMask = false;
8992     }else{
8993         var um = this.el.getUpdateManager();
8994         um.showLoadIndicator = false; // disable the default indicator
8995         um.on('beforeupdate', this.onBeforeLoad, this);
8996         um.on('update', this.onLoad, this);
8997         um.on('failure', this.onLoad, this);
8998         this.removeMask = true;
8999     }
9000 };
9001
9002 Roo.LoadMask.prototype = {
9003     /**
9004      * @cfg {Boolean} removeMask
9005      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9006      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9007      */
9008     removeMask : false,
9009     /**
9010      * @cfg {String} msg
9011      * The text to display in a centered loading message box (defaults to 'Loading...')
9012      */
9013     msg : 'Loading...',
9014     /**
9015      * @cfg {String} msgCls
9016      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9017      */
9018     msgCls : 'x-mask-loading',
9019
9020     /**
9021      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9022      * @type Boolean
9023      */
9024     disabled: false,
9025
9026     /**
9027      * Disables the mask to prevent it from being displayed
9028      */
9029     disable : function(){
9030        this.disabled = true;
9031     },
9032
9033     /**
9034      * Enables the mask so that it can be displayed
9035      */
9036     enable : function(){
9037         this.disabled = false;
9038     },
9039     
9040     onLoadException : function()
9041     {
9042         Roo.log(arguments);
9043         
9044         if (typeof(arguments[3]) != 'undefined') {
9045             Roo.MessageBox.alert("Error loading",arguments[3]);
9046         } 
9047         /*
9048         try {
9049             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9050                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9051             }   
9052         } catch(e) {
9053             
9054         }
9055         */
9056     
9057         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9058     },
9059     // private
9060     onLoad : function()
9061     {
9062         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9063     },
9064
9065     // private
9066     onBeforeLoad : function(){
9067         if(!this.disabled){
9068             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9069         }
9070     },
9071
9072     // private
9073     destroy : function(){
9074         if(this.store){
9075             this.store.un('beforeload', this.onBeforeLoad, this);
9076             this.store.un('load', this.onLoad, this);
9077             this.store.un('loadexception', this.onLoadException, this);
9078         }else{
9079             var um = this.el.getUpdateManager();
9080             um.un('beforeupdate', this.onBeforeLoad, this);
9081             um.un('update', this.onLoad, this);
9082             um.un('failure', this.onLoad, this);
9083         }
9084     }
9085 };/**
9086  * @class Roo.bootstrap.Table
9087  * @licence LGBL
9088  * @extends Roo.bootstrap.Component
9089  * @children Roo.bootstrap.TableBody
9090  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9091  * Similar to Roo.grid.Grid
9092  * <pre><code>
9093  var table = Roo.factory({
9094     xtype : 'Table',
9095     xns : Roo.bootstrap,
9096     autoSizeColumns: true,
9097     
9098     
9099     store : {
9100         xtype : 'Store',
9101         xns : Roo.data,
9102         remoteSort : true,
9103         sortInfo : { direction : 'ASC', field: 'name' },
9104         proxy : {
9105            xtype : 'HttpProxy',
9106            xns : Roo.data,
9107            method : 'GET',
9108            url : 'https://example.com/some.data.url.json'
9109         },
9110         reader : {
9111            xtype : 'JsonReader',
9112            xns : Roo.data,
9113            fields : [ 'id', 'name', whatever' ],
9114            id : 'id',
9115            root : 'data'
9116         }
9117     },
9118     cm : [
9119         {
9120             xtype : 'ColumnModel',
9121             xns : Roo.grid,
9122             align : 'center',
9123             cursor : 'pointer',
9124             dataIndex : 'is_in_group',
9125             header : "Name",
9126             sortable : true,
9127             renderer : function(v, x , r) {  
9128             
9129                 return String.format("{0}", v)
9130             }
9131             width : 3
9132         } // more columns..
9133     ],
9134     selModel : {
9135         xtype : 'RowSelectionModel',
9136         xns : Roo.bootstrap.Table
9137         // you can add listeners to catch selection change here....
9138     }
9139      
9140
9141  });
9142  // set any options
9143  grid.render(Roo.get("some-div"));
9144 </code></pre>
9145
9146 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9147
9148
9149
9150  *
9151  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9152  * @cfg {Roo.data.Store} store The data store to use
9153  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9154  * 
9155  * @cfg {String} cls table class
9156  *
9157  *
9158  * @cfg {string} empty_results  Text to display for no results 
9159  * @cfg {boolean} striped Should the rows be alternative striped
9160  * @cfg {boolean} bordered Add borders to the table
9161  * @cfg {boolean} hover Add hover highlighting
9162  * @cfg {boolean} condensed Format condensed
9163  * @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,
9164  *                also adds table-responsive (see bootstrap docs for details)
9165  * @cfg {Boolean} loadMask (true|false) default false
9166  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
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     headerShow : true,
9352     enableColumnResize: true,
9353     disableAutoSize: false,
9354   
9355     rowSelection : false,
9356     cellSelection : false,
9357     layout : false,
9358
9359     minColumnWidth : 50,
9360     
9361     // Roo.Element - the tbody
9362     bodyEl: false,  // <tbody> Roo.Element - thead element    
9363     headEl: false,  // <thead> Roo.Element - thead element
9364     resizeProxy : false, // proxy element for dragging?
9365
9366
9367     
9368     container: false, // used by gridpanel...
9369     
9370     lazyLoad : false,
9371     
9372     CSS : Roo.util.CSS,
9373     
9374     auto_hide_footer : false,
9375     
9376     view: false, // actually points to this..
9377     
9378     getAutoCreate : function()
9379     {
9380         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9381         
9382         cfg = {
9383             tag: 'table',
9384             cls : 'table', 
9385             cn : []
9386         };
9387         // this get's auto added by panel.Grid
9388         if (this.scrollBody) {
9389             cfg.cls += ' table-body-fixed';
9390         }    
9391         if (this.striped) {
9392             cfg.cls += ' table-striped';
9393         }
9394         
9395         if (this.hover) {
9396             cfg.cls += ' table-hover';
9397         }
9398         if (this.bordered) {
9399             cfg.cls += ' table-bordered';
9400         }
9401         if (this.condensed) {
9402             cfg.cls += ' table-condensed';
9403         }
9404         
9405         if (this.responsive) {
9406             cfg.cls += ' table-responsive';
9407         }
9408         
9409         if (this.cls) {
9410             cfg.cls+=  ' ' +this.cls;
9411         }
9412         
9413         
9414         
9415         if (this.layout) {
9416             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9417         }
9418         
9419         if(this.store || this.cm){
9420             if(this.headerShow){
9421                 cfg.cn.push(this.renderHeader());
9422             }
9423             
9424             cfg.cn.push(this.renderBody());
9425             
9426             if(this.footerShow){
9427                 cfg.cn.push(this.renderFooter());
9428             }
9429             // where does this come from?
9430             //cfg.cls+=  ' TableGrid';
9431         }
9432         
9433         return { cn : [ cfg ] };
9434     },
9435     
9436     initEvents : function()
9437     {   
9438         if(!this.store || !this.cm){
9439             return;
9440         }
9441         if (this.selModel) {
9442             this.selModel.initEvents();
9443         }
9444         
9445         
9446         //Roo.log('initEvents with ds!!!!');
9447         
9448         this.bodyEl = this.el.select('tbody', true).first();
9449         this.headEl = this.el.select('thead', true).first();
9450         this.mainFoot = this.el.select('tfoot', true).first();
9451         
9452         
9453         
9454         
9455         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9456             e.on('click', this.sort, this);
9457         }, this);
9458         
9459         
9460         // why is this done????? = it breaks dialogs??
9461         //this.parent().el.setStyle('position', 'relative');
9462         
9463         
9464         if (this.footer) {
9465             this.footer.parentId = this.id;
9466             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9467             
9468             if(this.lazyLoad){
9469                 this.el.select('tfoot tr td').first().addClass('hide');
9470             }
9471         } 
9472         
9473         if(this.loadMask) {
9474             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9475         }
9476         
9477         this.store.on('load', this.onLoad, this);
9478         this.store.on('beforeload', this.onBeforeLoad, this);
9479         this.store.on('update', this.onUpdate, this);
9480         this.store.on('add', this.onAdd, this);
9481         this.store.on("clear", this.clear, this);
9482         
9483         this.el.on("contextmenu", this.onContextMenu, this);
9484         
9485         
9486         this.cm.on("headerchange", this.onHeaderChange, this);
9487         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9488
9489  //?? does bodyEl get replaced on render?
9490         this.bodyEl.on("click", this.onClick, this);
9491         this.bodyEl.on("dblclick", this.onDblClick, this);        
9492         this.bodyEl.on('scroll', this.onBodyScroll, this);
9493
9494         // guessing mainbody will work - this relays usually caught by selmodel at present.
9495         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9496   
9497   
9498         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9499         
9500   
9501         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9502             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9503         }
9504         
9505         this.initCSS();
9506     },
9507     // Compatibility with grid - we implement all the view features at present.
9508     getView : function()
9509     {
9510         return this;
9511     },
9512     
9513     initCSS : function()
9514     {
9515         if(this.disableAutoSize) {
9516             return;
9517         }
9518         
9519         var cm = this.cm, styles = [];
9520         this.CSS.removeStyleSheet(this.id + '-cssrules');
9521         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9522         // we can honour xs/sm/md/xl  as widths...
9523         // we first have to decide what widht we are currently at...
9524         var sz = Roo.getGridSize();
9525         
9526         var total = 0;
9527         var last = -1;
9528         var cols = []; // visable cols.
9529         var total_abs = 0;
9530         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531             var w = cm.getColumnWidth(i, false);
9532             if(cm.isHidden(i)){
9533                 cols.push( { rel : false, abs : 0 });
9534                 continue;
9535             }
9536             if (w !== false) {
9537                 cols.push( { rel : false, abs : w });
9538                 total_abs += w;
9539                 last = i; // not really..
9540                 continue;
9541             }
9542             var w = cm.getColumnWidth(i, sz);
9543             if (w > 0) {
9544                 last = i
9545             }
9546             total += w;
9547             cols.push( { rel : w, abs : false });
9548         }
9549         
9550         var avail = this.bodyEl.dom.clientWidth - total_abs;
9551         
9552         var unitWidth = Math.floor(avail / total);
9553         var rem = avail - (unitWidth * total);
9554         
9555         var hidden, width, pos = 0 , splithide , left;
9556         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9557             
9558             hidden = 'display:none;';
9559             left = '';
9560             width  = 'width:0px;';
9561             splithide = '';
9562             if(!cm.isHidden(i)){
9563                 hidden = '';
9564                 
9565                 
9566                 // we can honour xs/sm/md/xl ?
9567                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9568                 if (w===0) {
9569                     hidden = 'display:none;';
9570                 }
9571                 // width should return a small number...
9572                 if (i == last) {
9573                     w+=rem; // add the remaining with..
9574                 }
9575                 pos += w;
9576                 left = "left:" + (pos -4) + "px;";
9577                 width = "width:" + w+ "px;";
9578                 
9579             }
9580             if (this.responsive) {
9581                 width = '';
9582                 left = '';
9583                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9584                 splithide = 'display: none;';
9585             }
9586             
9587             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9588             if (this.headEl) {
9589                 if (i == last) {
9590                     splithide = 'display:none;';
9591                 }
9592                 
9593                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9594                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9595                             // this is the popover version..
9596                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9597                 );
9598             }
9599             
9600         }
9601         //Roo.log(styles.join(''));
9602         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9603         
9604     },
9605     
9606     
9607     
9608     onContextMenu : function(e, t)
9609     {
9610         this.processEvent("contextmenu", e);
9611     },
9612     
9613     processEvent : function(name, e)
9614     {
9615         if (name != 'touchstart' ) {
9616             this.fireEvent(name, e);    
9617         }
9618         
9619         var t = e.getTarget();
9620         
9621         var cell = Roo.get(t);
9622         
9623         if(!cell){
9624             return;
9625         }
9626         
9627         if(cell.findParent('tfoot', false, true)){
9628             return;
9629         }
9630         
9631         if(cell.findParent('thead', false, true)){
9632             
9633             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9634                 cell = Roo.get(t).findParent('th', false, true);
9635                 if (!cell) {
9636                     Roo.log("failed to find th in thead?");
9637                     Roo.log(e.getTarget());
9638                     return;
9639                 }
9640             }
9641             
9642             var cellIndex = cell.dom.cellIndex;
9643             
9644             var ename = name == 'touchstart' ? 'click' : name;
9645             this.fireEvent("header" + ename, this, cellIndex, e);
9646             
9647             return;
9648         }
9649         
9650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9651             cell = Roo.get(t).findParent('td', false, true);
9652             if (!cell) {
9653                 Roo.log("failed to find th in tbody?");
9654                 Roo.log(e.getTarget());
9655                 return;
9656             }
9657         }
9658         
9659         var row = cell.findParent('tr', false, true);
9660         var cellIndex = cell.dom.cellIndex;
9661         var rowIndex = row.dom.rowIndex - 1;
9662         
9663         if(row !== false){
9664             
9665             this.fireEvent("row" + name, this, rowIndex, e);
9666             
9667             if(cell !== false){
9668             
9669                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9670             }
9671         }
9672         
9673     },
9674     
9675     onMouseover : function(e, el)
9676     {
9677         var cell = Roo.get(el);
9678         
9679         if(!cell){
9680             return;
9681         }
9682         
9683         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9684             cell = cell.findParent('td', false, true);
9685         }
9686         
9687         var row = cell.findParent('tr', false, true);
9688         var cellIndex = cell.dom.cellIndex;
9689         var rowIndex = row.dom.rowIndex - 1; // start from 0
9690         
9691         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9692         
9693     },
9694     
9695     onMouseout : function(e, el)
9696     {
9697         var cell = Roo.get(el);
9698         
9699         if(!cell){
9700             return;
9701         }
9702         
9703         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9704             cell = cell.findParent('td', false, true);
9705         }
9706         
9707         var row = cell.findParent('tr', false, true);
9708         var cellIndex = cell.dom.cellIndex;
9709         var rowIndex = row.dom.rowIndex - 1; // start from 0
9710         
9711         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9712         
9713     },
9714     
9715     onClick : function(e, el)
9716     {
9717         var cell = Roo.get(el);
9718         
9719         if(!cell || (!this.cellSelection && !this.rowSelection)){
9720             return;
9721         }
9722         
9723         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9724             cell = cell.findParent('td', false, true);
9725         }
9726         
9727         if(!cell || typeof(cell) == 'undefined'){
9728             return;
9729         }
9730         
9731         var row = cell.findParent('tr', false, true);
9732         
9733         if(!row || typeof(row) == 'undefined'){
9734             return;
9735         }
9736         
9737         var cellIndex = cell.dom.cellIndex;
9738         var rowIndex = this.getRowIndex(row);
9739         
9740         // why??? - should these not be based on SelectionModel?
9741         //if(this.cellSelection){
9742             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9743         //}
9744         
9745         //if(this.rowSelection){
9746             this.fireEvent('rowclick', this, row, rowIndex, e);
9747         //}
9748          
9749     },
9750         
9751     onDblClick : function(e,el)
9752     {
9753         var cell = Roo.get(el);
9754         
9755         if(!cell || (!this.cellSelection && !this.rowSelection)){
9756             return;
9757         }
9758         
9759         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9760             cell = cell.findParent('td', false, true);
9761         }
9762         
9763         if(!cell || typeof(cell) == 'undefined'){
9764             return;
9765         }
9766         
9767         var row = cell.findParent('tr', false, true);
9768         
9769         if(!row || typeof(row) == 'undefined'){
9770             return;
9771         }
9772         
9773         var cellIndex = cell.dom.cellIndex;
9774         var rowIndex = this.getRowIndex(row);
9775         
9776         if(this.cellSelection){
9777             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9778         }
9779         
9780         if(this.rowSelection){
9781             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9782         }
9783     },
9784     findRowIndex : function(el)
9785     {
9786         var cell = Roo.get(el);
9787         if(!cell) {
9788             return false;
9789         }
9790         var row = cell.findParent('tr', false, true);
9791         
9792         if(!row || typeof(row) == 'undefined'){
9793             return false;
9794         }
9795         return this.getRowIndex(row);
9796     },
9797     sort : function(e,el)
9798     {
9799         var col = Roo.get(el);
9800         
9801         if(!col.hasClass('sortable')){
9802             return;
9803         }
9804         
9805         var sort = col.attr('sort');
9806         var dir = 'ASC';
9807         
9808         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9809             dir = 'DESC';
9810         }
9811         
9812         this.store.sortInfo = {field : sort, direction : dir};
9813         
9814         if (this.footer) {
9815             Roo.log("calling footer first");
9816             this.footer.onClick('first');
9817         } else {
9818         
9819             this.store.load({ params : { start : 0 } });
9820         }
9821     },
9822     
9823     renderHeader : function()
9824     {
9825         var header = {
9826             tag: 'thead',
9827             cn : []
9828         };
9829         
9830         var cm = this.cm;
9831         this.totalWidth = 0;
9832         
9833         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9834             
9835             var config = cm.config[i];
9836             
9837             var c = {
9838                 tag: 'th',
9839                 cls : 'x-hcol-' + i,
9840                 style : '',
9841                 
9842                 html: cm.getColumnHeader(i)
9843             };
9844             
9845             var tooltip = cm.getColumnTooltip(i);
9846             if (tooltip) {
9847                 c.tooltip = tooltip;
9848             }
9849             
9850             
9851             var hh = '';
9852             
9853             if(typeof(config.sortable) != 'undefined' && config.sortable){
9854                 c.cls += ' sortable';
9855                 c.html = '<i class="fa"></i>' + c.html;
9856             }
9857             
9858             // could use BS4 hidden-..-down 
9859             
9860             if(typeof(config.lgHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.mdHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.smHeader) != 'undefined'){
9869                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9870             }
9871             
9872             if(typeof(config.xsHeader) != 'undefined'){
9873                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9874             }
9875             
9876             if(hh.length){
9877                 c.html = hh;
9878             }
9879             
9880             if(typeof(config.tooltip) != 'undefined'){
9881                 c.tooltip = config.tooltip;
9882             }
9883             
9884             if(typeof(config.colspan) != 'undefined'){
9885                 c.colspan = config.colspan;
9886             }
9887             
9888             // hidden is handled by CSS now
9889             
9890             if(typeof(config.dataIndex) != 'undefined'){
9891                 c.sort = config.dataIndex;
9892             }
9893             
9894            
9895             
9896             if(typeof(config.align) != 'undefined' && config.align.length){
9897                 c.style += ' text-align:' + config.align + ';';
9898             }
9899             
9900             /* width is done in CSS
9901              *if(typeof(config.width) != 'undefined'){
9902                 c.style += ' width:' + config.width + 'px;';
9903                 this.totalWidth += config.width;
9904             } else {
9905                 this.totalWidth += 100; // assume minimum of 100 per column?
9906             }
9907             */
9908             
9909             if(typeof(config.cls) != 'undefined'){
9910                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9911             }
9912             // this is the bit that doesnt reall work at all...
9913             
9914             if (this.responsive) {
9915                  
9916             
9917                 ['xs','sm','md','lg'].map(function(size){
9918                     
9919                     if(typeof(config[size]) == 'undefined'){
9920                         return;
9921                     }
9922                      
9923                     if (!config[size]) { // 0 = hidden
9924                         // BS 4 '0' is treated as hide that column and below.
9925                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9926                         return;
9927                     }
9928                     
9929                     c.cls += ' col-' + size + '-' + config[size] + (
9930                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9931                     );
9932                     
9933                     
9934                 });
9935             }
9936             // at the end?
9937             
9938             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9939             
9940             
9941             
9942             
9943             header.cn.push(c)
9944         }
9945         
9946         return header;
9947     },
9948     
9949     renderBody : function()
9950     {
9951         var body = {
9952             tag: 'tbody',
9953             cn : [
9954                 {
9955                     tag: 'tr',
9956                     cn : [
9957                         {
9958                             tag : 'td',
9959                             colspan :  this.cm.getColumnCount()
9960                         }
9961                     ]
9962                 }
9963             ]
9964         };
9965         
9966         return body;
9967     },
9968     
9969     renderFooter : function()
9970     {
9971         var footer = {
9972             tag: 'tfoot',
9973             cn : [
9974                 {
9975                     tag: 'tr',
9976                     cn : [
9977                         {
9978                             tag : 'td',
9979                             colspan :  this.cm.getColumnCount()
9980                         }
9981                     ]
9982                 }
9983             ]
9984         };
9985         
9986         return footer;
9987     },
9988     
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.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         Roo.each(this.el.select('tbody td', true).elements, function(e){
10049             e.on('mouseover', _this.onMouseover, _this);
10050         });
10051         
10052         Roo.each(this.el.select('tbody td', true).elements, function(e){
10053             e.on('mouseout', _this.onMouseout, _this);
10054         });
10055         this.fireEvent('rowsrendered', this);
10056         
10057         this.autoSize();
10058         
10059         this.initCSS(); /// resize cols
10060
10061         
10062     },
10063     
10064     
10065     onUpdate : function(ds,record)
10066     {
10067         this.refreshRow(record);
10068         this.autoSize();
10069     },
10070     
10071     onRemove : function(ds, record, index, isUpdate){
10072         if(isUpdate !== true){
10073             this.fireEvent("beforerowremoved", this, index, record);
10074         }
10075         var bt = this.bodyEl.dom;
10076         
10077         var rows = this.el.select('tbody > tr', true).elements;
10078         
10079         if(typeof(rows[index]) != 'undefined'){
10080             bt.removeChild(rows[index].dom);
10081         }
10082         
10083 //        if(bt.rows[index]){
10084 //            bt.removeChild(bt.rows[index]);
10085 //        }
10086         
10087         if(isUpdate !== true){
10088             //this.stripeRows(index);
10089             //this.syncRowHeights(index, index);
10090             //this.layout();
10091             this.fireEvent("rowremoved", this, index, record);
10092         }
10093     },
10094     
10095     onAdd : function(ds, records, rowIndex)
10096     {
10097         //Roo.log('on Add called');
10098         // - note this does not handle multiple adding very well..
10099         var bt = this.bodyEl.dom;
10100         for (var i =0 ; i < records.length;i++) {
10101             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10102             //Roo.log(records[i]);
10103             //Roo.log(this.store.getAt(rowIndex+i));
10104             this.insertRow(this.store, rowIndex + i, false);
10105             return;
10106         }
10107         
10108     },
10109     
10110     
10111     refreshRow : function(record){
10112         var ds = this.store, index;
10113         if(typeof record == 'number'){
10114             index = record;
10115             record = ds.getAt(index);
10116         }else{
10117             index = ds.indexOf(record);
10118             if (index < 0) {
10119                 return; // should not happen - but seems to 
10120             }
10121         }
10122         this.insertRow(ds, index, true);
10123         this.autoSize();
10124         this.onRemove(ds, record, index+1, true);
10125         this.autoSize();
10126         //this.syncRowHeights(index, index);
10127         //this.layout();
10128         this.fireEvent("rowupdated", this, index, record);
10129     },
10130     // private - called by RowSelection
10131     onRowSelect : function(rowIndex){
10132         var row = this.getRowDom(rowIndex);
10133         row.addClass(['bg-info','info']);
10134     },
10135     // private - called by RowSelection
10136     onRowDeselect : function(rowIndex)
10137     {
10138         if (rowIndex < 0) {
10139             return;
10140         }
10141         var row = this.getRowDom(rowIndex);
10142         row.removeClass(['bg-info','info']);
10143     },
10144       /**
10145      * Focuses the specified row.
10146      * @param {Number} row The row index
10147      */
10148     focusRow : function(row)
10149     {
10150         //Roo.log('GridView.focusRow');
10151         var x = this.bodyEl.dom.scrollLeft;
10152         this.focusCell(row, 0, false);
10153         this.bodyEl.dom.scrollLeft = x;
10154
10155     },
10156      /**
10157      * Focuses the specified cell.
10158      * @param {Number} row The row index
10159      * @param {Number} col The column index
10160      * @param {Boolean} hscroll false to disable horizontal scrolling
10161      */
10162     focusCell : function(row, col, hscroll)
10163     {
10164         //Roo.log('GridView.focusCell');
10165         var el = this.ensureVisible(row, col, hscroll);
10166         // not sure what focusEL achives = it's a <a> pos relative 
10167         //this.focusEl.alignTo(el, "tl-tl");
10168         //if(Roo.isGecko){
10169         //    this.focusEl.focus();
10170         //}else{
10171         //    this.focusEl.focus.defer(1, this.focusEl);
10172         //}
10173     },
10174     
10175      /**
10176      * Scrolls the specified cell into view
10177      * @param {Number} row The row index
10178      * @param {Number} col The column index
10179      * @param {Boolean} hscroll false to disable horizontal scrolling
10180      */
10181     ensureVisible : function(row, col, hscroll)
10182     {
10183         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10184         //return null; //disable for testing.
10185         if(typeof row != "number"){
10186             row = row.rowIndex;
10187         }
10188         if(row < 0 && row >= this.ds.getCount()){
10189             return  null;
10190         }
10191         col = (col !== undefined ? col : 0);
10192         var cm = this.cm;
10193         while(cm.isHidden(col)){
10194             col++;
10195         }
10196
10197         var el = this.getCellDom(row, col);
10198         if(!el){
10199             return null;
10200         }
10201         var c = this.bodyEl.dom;
10202
10203         var ctop = parseInt(el.offsetTop, 10);
10204         var cleft = parseInt(el.offsetLeft, 10);
10205         var cbot = ctop + el.offsetHeight;
10206         var cright = cleft + el.offsetWidth;
10207
10208         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10209         var ch = 0; //?? header is not withing the area?
10210         var stop = parseInt(c.scrollTop, 10);
10211         var sleft = parseInt(c.scrollLeft, 10);
10212         var sbot = stop + ch;
10213         var sright = sleft + c.clientWidth;
10214         /*
10215         Roo.log('GridView.ensureVisible:' +
10216                 ' ctop:' + ctop +
10217                 ' c.clientHeight:' + c.clientHeight +
10218                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10219                 ' stop:' + stop +
10220                 ' cbot:' + cbot +
10221                 ' sbot:' + sbot +
10222                 ' ch:' + ch  
10223                 );
10224         */
10225         if(ctop < stop){
10226             c.scrollTop = ctop;
10227             //Roo.log("set scrolltop to ctop DISABLE?");
10228         }else if(cbot > sbot){
10229             //Roo.log("set scrolltop to cbot-ch");
10230             c.scrollTop = cbot-ch;
10231         }
10232
10233         if(hscroll !== false){
10234             if(cleft < sleft){
10235                 c.scrollLeft = cleft;
10236             }else if(cright > sright){
10237                 c.scrollLeft = cright-c.clientWidth;
10238             }
10239         }
10240
10241         return el;
10242     },
10243     
10244     
10245     insertRow : function(dm, rowIndex, isUpdate){
10246         
10247         if(!isUpdate){
10248             this.fireEvent("beforerowsinserted", this, rowIndex);
10249         }
10250             //var s = this.getScrollState();
10251         var row = this.renderRow(this.cm, this.store, rowIndex);
10252         // insert before rowIndex..
10253         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10254         
10255         var _this = this;
10256                 
10257         if(row.cellObjects.length){
10258             Roo.each(row.cellObjects, function(r){
10259                 _this.renderCellObject(r);
10260             })
10261         }
10262             
10263         if(!isUpdate){
10264             this.fireEvent("rowsinserted", this, rowIndex);
10265             //this.syncRowHeights(firstRow, lastRow);
10266             //this.stripeRows(firstRow);
10267             //this.layout();
10268         }
10269         
10270     },
10271     
10272     
10273     getRowDom : function(rowIndex)
10274     {
10275         var rows = this.el.select('tbody > tr', true).elements;
10276         
10277         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10278         
10279     },
10280     getCellDom : function(rowIndex, colIndex)
10281     {
10282         var row = this.getRowDom(rowIndex);
10283         if (row === false) {
10284             return false;
10285         }
10286         var cols = row.select('td', true).elements;
10287         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10288         
10289     },
10290     
10291     // returns the object tree for a tr..
10292   
10293     
10294     renderRow : function(cm, ds, rowIndex) 
10295     {
10296         var d = ds.getAt(rowIndex);
10297         
10298         var row = {
10299             tag : 'tr',
10300             cls : 'x-row-' + rowIndex,
10301             cn : []
10302         };
10303             
10304         var cellObjects = [];
10305         
10306         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10307             var config = cm.config[i];
10308             
10309             var renderer = cm.getRenderer(i);
10310             var value = '';
10311             var id = false;
10312             
10313             if(typeof(renderer) !== 'undefined'){
10314                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10315             }
10316             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10317             // and are rendered into the cells after the row is rendered - using the id for the element.
10318             
10319             if(typeof(value) === 'object'){
10320                 id = Roo.id();
10321                 cellObjects.push({
10322                     container : id,
10323                     cfg : value 
10324                 })
10325             }
10326             
10327             var rowcfg = {
10328                 record: d,
10329                 rowIndex : rowIndex,
10330                 colIndex : i,
10331                 rowClass : ''
10332             };
10333
10334             this.fireEvent('rowclass', this, rowcfg);
10335             
10336             var td = {
10337                 tag: 'td',
10338                 // this might end up displaying HTML?
10339                 // this is too messy... - better to only do it on columsn you know are going to be too long
10340                 //tooltip : (typeof(value) === 'object') ? '' : value,
10341                 cls : rowcfg.rowClass + ' x-col-' + i,
10342                 style: '',
10343                 html: (typeof(value) === 'object') ? '' : value
10344             };
10345             
10346             if (id) {
10347                 td.id = id;
10348             }
10349             
10350             if(typeof(config.colspan) != 'undefined'){
10351                 td.colspan = config.colspan;
10352             }
10353             
10354             
10355             
10356             if(typeof(config.align) != 'undefined' && config.align.length){
10357                 td.style += ' text-align:' + config.align + ';';
10358             }
10359             if(typeof(config.valign) != 'undefined' && config.valign.length){
10360                 td.style += ' vertical-align:' + config.valign + ';';
10361             }
10362             /*
10363             if(typeof(config.width) != 'undefined'){
10364                 td.style += ' width:' +  config.width + 'px;';
10365             }
10366             */
10367             
10368             if(typeof(config.cursor) != 'undefined'){
10369                 td.style += ' cursor:' +  config.cursor + ';';
10370             }
10371             
10372             if(typeof(config.cls) != 'undefined'){
10373                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10374             }
10375             if (this.responsive) {
10376                 ['xs','sm','md','lg'].map(function(size){
10377                     
10378                     if(typeof(config[size]) == 'undefined'){
10379                         return;
10380                     }
10381                     
10382                     
10383                       
10384                     if (!config[size]) { // 0 = hidden
10385                         // BS 4 '0' is treated as hide that column and below.
10386                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10387                         return;
10388                     }
10389                     
10390                     td.cls += ' col-' + size + '-' + config[size] + (
10391                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10392                     );
10393                      
10394     
10395                 });
10396             }
10397             row.cn.push(td);
10398            
10399         }
10400         
10401         row.cellObjects = cellObjects;
10402         
10403         return row;
10404           
10405     },
10406     
10407     
10408     
10409     onBeforeLoad : function()
10410     {
10411         this.el.unmask(); // if needed.
10412     },
10413      /**
10414      * Remove all rows
10415      */
10416     clear : function()
10417     {
10418         this.el.select('tbody', true).first().dom.innerHTML = '';
10419     },
10420     /**
10421      * Show or hide a row.
10422      * @param {Number} rowIndex to show or hide
10423      * @param {Boolean} state hide
10424      */
10425     setRowVisibility : function(rowIndex, state)
10426     {
10427         var bt = this.bodyEl.dom;
10428         
10429         var rows = this.el.select('tbody > tr', true).elements;
10430         
10431         if(typeof(rows[rowIndex]) == 'undefined'){
10432             return;
10433         }
10434         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10435         
10436     },
10437     
10438     
10439     getSelectionModel : function(){
10440         if(!this.selModel){
10441             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10442         }
10443         return this.selModel;
10444     },
10445     /*
10446      * Render the Roo.bootstrap object from renderder
10447      */
10448     renderCellObject : function(r)
10449     {
10450         var _this = this;
10451         
10452         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10453         
10454         var t = r.cfg.render(r.container);
10455         
10456         if(r.cfg.cn){
10457             Roo.each(r.cfg.cn, function(c){
10458                 var child = {
10459                     container: t.getChildContainer(),
10460                     cfg: c
10461                 };
10462                 _this.renderCellObject(child);
10463             })
10464         }
10465     },
10466     /**
10467      * get the Row Index from a dom element.
10468      * @param {Roo.Element} row The row to look for
10469      * @returns {Number} the row
10470      */
10471     getRowIndex : function(row)
10472     {
10473         var rowIndex = -1;
10474         
10475         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10476             if(el != row){
10477                 return;
10478             }
10479             
10480             rowIndex = index;
10481         });
10482         
10483         return rowIndex;
10484     },
10485     /**
10486      * get the header TH element for columnIndex
10487      * @param {Number} columnIndex
10488      * @returns {Roo.Element}
10489      */
10490     getHeaderIndex: function(colIndex)
10491     {
10492         var cols = this.headEl.select('th', true).elements;
10493         return cols[colIndex]; 
10494     },
10495     /**
10496      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10497      * @param {domElement} cell to look for
10498      * @returns {Number} the column
10499      */
10500     getCellIndex : function(cell)
10501     {
10502         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10503         if(id){
10504             return parseInt(id[1], 10);
10505         }
10506         return 0;
10507     },
10508      /**
10509      * Returns the grid's underlying element = used by panel.Grid
10510      * @return {Element} The element
10511      */
10512     getGridEl : function(){
10513         return this.el;
10514     },
10515      /**
10516      * Forces a resize - used by panel.Grid
10517      * @return {Element} The element
10518      */
10519     autoSize : function()
10520     {
10521         if(this.disableAutoSize) {
10522             return;
10523         }
10524         //var ctr = Roo.get(this.container.dom.parentElement);
10525         var ctr = Roo.get(this.el.dom);
10526         
10527         var thd = this.getGridEl().select('thead',true).first();
10528         var tbd = this.getGridEl().select('tbody', true).first();
10529         var tfd = this.getGridEl().select('tfoot', true).first();
10530         
10531         var cw = ctr.getWidth();
10532         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10533         
10534         if (tbd) {
10535             
10536             tbd.setWidth(ctr.getWidth());
10537             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10538             // this needs fixing for various usage - currently only hydra job advers I think..
10539             //tdb.setHeight(
10540             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10541             //); 
10542             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10543             cw -= barsize;
10544         }
10545         cw = Math.max(cw, this.totalWidth);
10546         this.getGridEl().select('tbody tr',true).setWidth(cw);
10547         this.initCSS();
10548         
10549         // resize 'expandable coloumn?
10550         
10551         return; // we doe not have a view in this design..
10552         
10553     },
10554     onBodyScroll: function()
10555     {
10556         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10557         if(this.headEl){
10558             this.headEl.setStyle({
10559                 'position' : 'relative',
10560                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10561             });
10562         }
10563         
10564         if(this.lazyLoad){
10565             
10566             var scrollHeight = this.bodyEl.dom.scrollHeight;
10567             
10568             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10569             
10570             var height = this.bodyEl.getHeight();
10571             
10572             if(scrollHeight - height == scrollTop) {
10573                 
10574                 var total = this.ds.getTotalCount();
10575                 
10576                 if(this.footer.cursor + this.footer.pageSize < total){
10577                     
10578                     this.footer.ds.load({
10579                         params : {
10580                             start : this.footer.cursor + this.footer.pageSize,
10581                             limit : this.footer.pageSize
10582                         },
10583                         add : true
10584                     });
10585                 }
10586             }
10587             
10588         }
10589     },
10590     onColumnSplitterMoved : function(i, diff)
10591     {
10592         this.userResized = true;
10593         
10594         var cm = this.colModel;
10595         
10596         var w = this.getHeaderIndex(i).getWidth() + diff;
10597         
10598         
10599         cm.setColumnWidth(i, w, true);
10600         this.initCSS();
10601         //var cid = cm.getColumnId(i); << not used in this version?
10602        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10603         
10604         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10605         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10606         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10607 */
10608         //this.updateSplitters();
10609         //this.layout(); << ??
10610         this.fireEvent("columnresize", i, w);
10611     },
10612     onHeaderChange : function()
10613     {
10614         var header = this.renderHeader();
10615         var table = this.el.select('table', true).first();
10616         
10617         this.headEl.remove();
10618         this.headEl = table.createChild(header, this.bodyEl, false);
10619         
10620         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10621             e.on('click', this.sort, this);
10622         }, this);
10623         
10624         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10625             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10626         }
10627         
10628     },
10629     
10630     onHiddenChange : function(colModel, colIndex, hidden)
10631     {
10632         /*
10633         this.cm.setHidden()
10634         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10635         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10636         
10637         this.CSS.updateRule(thSelector, "display", "");
10638         this.CSS.updateRule(tdSelector, "display", "");
10639         
10640         if(hidden){
10641             this.CSS.updateRule(thSelector, "display", "none");
10642             this.CSS.updateRule(tdSelector, "display", "none");
10643         }
10644         */
10645         // onload calls initCSS()
10646         this.onHeaderChange();
10647         this.onLoad();
10648     },
10649     
10650     setColumnWidth: function(col_index, width)
10651     {
10652         // width = "md-2 xs-2..."
10653         if(!this.colModel.config[col_index]) {
10654             return;
10655         }
10656         
10657         var w = width.split(" ");
10658         
10659         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10660         
10661         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10662         
10663         
10664         for(var j = 0; j < w.length; j++) {
10665             
10666             if(!w[j]) {
10667                 continue;
10668             }
10669             
10670             var size_cls = w[j].split("-");
10671             
10672             if(!Number.isInteger(size_cls[1] * 1)) {
10673                 continue;
10674             }
10675             
10676             if(!this.colModel.config[col_index][size_cls[0]]) {
10677                 continue;
10678             }
10679             
10680             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10681                 continue;
10682             }
10683             
10684             h_row[0].classList.replace(
10685                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10686                 "col-"+size_cls[0]+"-"+size_cls[1]
10687             );
10688             
10689             for(var i = 0; i < rows.length; i++) {
10690                 
10691                 var size_cls = w[j].split("-");
10692                 
10693                 if(!Number.isInteger(size_cls[1] * 1)) {
10694                     continue;
10695                 }
10696                 
10697                 if(!this.colModel.config[col_index][size_cls[0]]) {
10698                     continue;
10699                 }
10700                 
10701                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10702                     continue;
10703                 }
10704                 
10705                 rows[i].classList.replace(
10706                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10707                     "col-"+size_cls[0]+"-"+size_cls[1]
10708                 );
10709             }
10710             
10711             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10712         }
10713     }
10714 });
10715
10716 // currently only used to find the split on drag.. 
10717 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10718
10719 /**
10720  * @depricated
10721 */
10722 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10723 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10724 /*
10725  * - LGPL
10726  *
10727  * table cell
10728  * 
10729  */
10730
10731 /**
10732  * @class Roo.bootstrap.TableCell
10733  * @extends Roo.bootstrap.Component
10734  * @children Roo.bootstrap.Component
10735  * @parent Roo.bootstrap.TableRow
10736  * Bootstrap TableCell class
10737  * 
10738  * @cfg {String} html cell contain text
10739  * @cfg {String} cls cell class
10740  * @cfg {String} tag cell tag (td|th) default td
10741  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10742  * @cfg {String} align Aligns the content in a cell
10743  * @cfg {String} axis Categorizes cells
10744  * @cfg {String} bgcolor Specifies the background color of a cell
10745  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10746  * @cfg {Number} colspan Specifies the number of columns a cell should span
10747  * @cfg {String} headers Specifies one or more header cells a cell is related to
10748  * @cfg {Number} height Sets the height of a cell
10749  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10750  * @cfg {Number} rowspan Sets the number of rows a cell should span
10751  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10752  * @cfg {String} valign Vertical aligns the content in a cell
10753  * @cfg {Number} width Specifies the width of a cell
10754  * 
10755  * @constructor
10756  * Create a new TableCell
10757  * @param {Object} config The config object
10758  */
10759
10760 Roo.bootstrap.TableCell = function(config){
10761     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10762 };
10763
10764 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10765     
10766     html: false,
10767     cls: false,
10768     tag: false,
10769     abbr: false,
10770     align: false,
10771     axis: false,
10772     bgcolor: false,
10773     charoff: false,
10774     colspan: false,
10775     headers: false,
10776     height: false,
10777     nowrap: false,
10778     rowspan: false,
10779     scope: false,
10780     valign: false,
10781     width: false,
10782     
10783     
10784     getAutoCreate : function(){
10785         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10786         
10787         cfg = {
10788             tag: 'td'
10789         };
10790         
10791         if(this.tag){
10792             cfg.tag = this.tag;
10793         }
10794         
10795         if (this.html) {
10796             cfg.html=this.html
10797         }
10798         if (this.cls) {
10799             cfg.cls=this.cls
10800         }
10801         if (this.abbr) {
10802             cfg.abbr=this.abbr
10803         }
10804         if (this.align) {
10805             cfg.align=this.align
10806         }
10807         if (this.axis) {
10808             cfg.axis=this.axis
10809         }
10810         if (this.bgcolor) {
10811             cfg.bgcolor=this.bgcolor
10812         }
10813         if (this.charoff) {
10814             cfg.charoff=this.charoff
10815         }
10816         if (this.colspan) {
10817             cfg.colspan=this.colspan
10818         }
10819         if (this.headers) {
10820             cfg.headers=this.headers
10821         }
10822         if (this.height) {
10823             cfg.height=this.height
10824         }
10825         if (this.nowrap) {
10826             cfg.nowrap=this.nowrap
10827         }
10828         if (this.rowspan) {
10829             cfg.rowspan=this.rowspan
10830         }
10831         if (this.scope) {
10832             cfg.scope=this.scope
10833         }
10834         if (this.valign) {
10835             cfg.valign=this.valign
10836         }
10837         if (this.width) {
10838             cfg.width=this.width
10839         }
10840         
10841         
10842         return cfg;
10843     }
10844    
10845 });
10846
10847  
10848
10849  /*
10850  * - LGPL
10851  *
10852  * table row
10853  * 
10854  */
10855
10856 /**
10857  * @class Roo.bootstrap.TableRow
10858  * @extends Roo.bootstrap.Component
10859  * @children Roo.bootstrap.TableCell
10860  * @parent Roo.bootstrap.TableBody
10861  * Bootstrap TableRow class
10862  * @cfg {String} cls row class
10863  * @cfg {String} align Aligns the content in a table row
10864  * @cfg {String} bgcolor Specifies a background color for a table row
10865  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10866  * @cfg {String} valign Vertical aligns the content in a table row
10867  * 
10868  * @constructor
10869  * Create a new TableRow
10870  * @param {Object} config The config object
10871  */
10872
10873 Roo.bootstrap.TableRow = function(config){
10874     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10875 };
10876
10877 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10878     
10879     cls: false,
10880     align: false,
10881     bgcolor: false,
10882     charoff: false,
10883     valign: false,
10884     
10885     getAutoCreate : function(){
10886         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10887         
10888         cfg = {
10889             tag: 'tr'
10890         };
10891             
10892         if(this.cls){
10893             cfg.cls = this.cls;
10894         }
10895         if(this.align){
10896             cfg.align = this.align;
10897         }
10898         if(this.bgcolor){
10899             cfg.bgcolor = this.bgcolor;
10900         }
10901         if(this.charoff){
10902             cfg.charoff = this.charoff;
10903         }
10904         if(this.valign){
10905             cfg.valign = this.valign;
10906         }
10907         
10908         return cfg;
10909     }
10910    
10911 });
10912
10913  
10914
10915  /*
10916  * - LGPL
10917  *
10918  * table body
10919  * 
10920  */
10921
10922 /**
10923  * @class Roo.bootstrap.TableBody
10924  * @extends Roo.bootstrap.Component
10925  * @children Roo.bootstrap.TableRow
10926  * @parent Roo.bootstrap.Table
10927  * Bootstrap TableBody class
10928  * @cfg {String} cls element class
10929  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10930  * @cfg {String} align Aligns the content inside the element
10931  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10932  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10933  * 
10934  * @constructor
10935  * Create a new TableBody
10936  * @param {Object} config The config object
10937  */
10938
10939 Roo.bootstrap.TableBody = function(config){
10940     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10941 };
10942
10943 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10944     
10945     cls: false,
10946     tag: false,
10947     align: false,
10948     charoff: false,
10949     valign: false,
10950     
10951     getAutoCreate : function(){
10952         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10953         
10954         cfg = {
10955             tag: 'tbody'
10956         };
10957             
10958         if (this.cls) {
10959             cfg.cls=this.cls
10960         }
10961         if(this.tag){
10962             cfg.tag = this.tag;
10963         }
10964         
10965         if(this.align){
10966             cfg.align = this.align;
10967         }
10968         if(this.charoff){
10969             cfg.charoff = this.charoff;
10970         }
10971         if(this.valign){
10972             cfg.valign = this.valign;
10973         }
10974         
10975         return cfg;
10976     }
10977     
10978     
10979 //    initEvents : function()
10980 //    {
10981 //        
10982 //        if(!this.store){
10983 //            return;
10984 //        }
10985 //        
10986 //        this.store = Roo.factory(this.store, Roo.data);
10987 //        this.store.on('load', this.onLoad, this);
10988 //        
10989 //        this.store.load();
10990 //        
10991 //    },
10992 //    
10993 //    onLoad: function () 
10994 //    {   
10995 //        this.fireEvent('load', this);
10996 //    }
10997 //    
10998 //   
10999 });
11000
11001  
11002
11003  /*
11004  * Based on:
11005  * Ext JS Library 1.1.1
11006  * Copyright(c) 2006-2007, Ext JS, LLC.
11007  *
11008  * Originally Released Under LGPL - original licence link has changed is not relivant.
11009  *
11010  * Fork - LGPL
11011  * <script type="text/javascript">
11012  */
11013
11014 // as we use this in bootstrap.
11015 Roo.namespace('Roo.form');
11016  /**
11017  * @class Roo.form.Action
11018  * Internal Class used to handle form actions
11019  * @constructor
11020  * @param {Roo.form.BasicForm} el The form element or its id
11021  * @param {Object} config Configuration options
11022  */
11023
11024  
11025  
11026 // define the action interface
11027 Roo.form.Action = function(form, options){
11028     this.form = form;
11029     this.options = options || {};
11030 };
11031 /**
11032  * Client Validation Failed
11033  * @const 
11034  */
11035 Roo.form.Action.CLIENT_INVALID = 'client';
11036 /**
11037  * Server Validation Failed
11038  * @const 
11039  */
11040 Roo.form.Action.SERVER_INVALID = 'server';
11041  /**
11042  * Connect to Server Failed
11043  * @const 
11044  */
11045 Roo.form.Action.CONNECT_FAILURE = 'connect';
11046 /**
11047  * Reading Data from Server Failed
11048  * @const 
11049  */
11050 Roo.form.Action.LOAD_FAILURE = 'load';
11051
11052 Roo.form.Action.prototype = {
11053     type : 'default',
11054     failureType : undefined,
11055     response : undefined,
11056     result : undefined,
11057
11058     // interface method
11059     run : function(options){
11060
11061     },
11062
11063     // interface method
11064     success : function(response){
11065
11066     },
11067
11068     // interface method
11069     handleResponse : function(response){
11070
11071     },
11072
11073     // default connection failure
11074     failure : function(response){
11075         
11076         this.response = response;
11077         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11078         this.form.afterAction(this, false);
11079     },
11080
11081     processResponse : function(response){
11082         this.response = response;
11083         if(!response.responseText){
11084             return true;
11085         }
11086         this.result = this.handleResponse(response);
11087         return this.result;
11088     },
11089
11090     // utility functions used internally
11091     getUrl : function(appendParams){
11092         var url = this.options.url || this.form.url || this.form.el.dom.action;
11093         if(appendParams){
11094             var p = this.getParams();
11095             if(p){
11096                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11097             }
11098         }
11099         return url;
11100     },
11101
11102     getMethod : function(){
11103         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11104     },
11105
11106     getParams : function(){
11107         var bp = this.form.baseParams;
11108         var p = this.options.params;
11109         if(p){
11110             if(typeof p == "object"){
11111                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11112             }else if(typeof p == 'string' && bp){
11113                 p += '&' + Roo.urlEncode(bp);
11114             }
11115         }else if(bp){
11116             p = Roo.urlEncode(bp);
11117         }
11118         return p;
11119     },
11120
11121     createCallback : function(){
11122         return {
11123             success: this.success,
11124             failure: this.failure,
11125             scope: this,
11126             timeout: (this.form.timeout*1000),
11127             upload: this.form.fileUpload ? this.success : undefined
11128         };
11129     }
11130 };
11131
11132 Roo.form.Action.Submit = function(form, options){
11133     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11134 };
11135
11136 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11137     type : 'submit',
11138
11139     haveProgress : false,
11140     uploadComplete : false,
11141     
11142     // uploadProgress indicator.
11143     uploadProgress : function()
11144     {
11145         if (!this.form.progressUrl) {
11146             return;
11147         }
11148         
11149         if (!this.haveProgress) {
11150             Roo.MessageBox.progress("Uploading", "Uploading");
11151         }
11152         if (this.uploadComplete) {
11153            Roo.MessageBox.hide();
11154            return;
11155         }
11156         
11157         this.haveProgress = true;
11158    
11159         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11160         
11161         var c = new Roo.data.Connection();
11162         c.request({
11163             url : this.form.progressUrl,
11164             params: {
11165                 id : uid
11166             },
11167             method: 'GET',
11168             success : function(req){
11169                //console.log(data);
11170                 var rdata = false;
11171                 var edata;
11172                 try  {
11173                    rdata = Roo.decode(req.responseText)
11174                 } catch (e) {
11175                     Roo.log("Invalid data from server..");
11176                     Roo.log(edata);
11177                     return;
11178                 }
11179                 if (!rdata || !rdata.success) {
11180                     Roo.log(rdata);
11181                     Roo.MessageBox.alert(Roo.encode(rdata));
11182                     return;
11183                 }
11184                 var data = rdata.data;
11185                 
11186                 if (this.uploadComplete) {
11187                    Roo.MessageBox.hide();
11188                    return;
11189                 }
11190                    
11191                 if (data){
11192                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11193                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11194                     );
11195                 }
11196                 this.uploadProgress.defer(2000,this);
11197             },
11198        
11199             failure: function(data) {
11200                 Roo.log('progress url failed ');
11201                 Roo.log(data);
11202             },
11203             scope : this
11204         });
11205            
11206     },
11207     
11208     
11209     run : function()
11210     {
11211         // run get Values on the form, so it syncs any secondary forms.
11212         this.form.getValues();
11213         
11214         var o = this.options;
11215         var method = this.getMethod();
11216         var isPost = method == 'POST';
11217         if(o.clientValidation === false || this.form.isValid()){
11218             
11219             if (this.form.progressUrl) {
11220                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11221                     (new Date() * 1) + '' + Math.random());
11222                     
11223             } 
11224             
11225             
11226             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11227                 form:this.form.el.dom,
11228                 url:this.getUrl(!isPost),
11229                 method: method,
11230                 params:isPost ? this.getParams() : null,
11231                 isUpload: this.form.fileUpload,
11232                 formData : this.form.formData
11233             }));
11234             
11235             this.uploadProgress();
11236
11237         }else if (o.clientValidation !== false){ // client validation failed
11238             this.failureType = Roo.form.Action.CLIENT_INVALID;
11239             this.form.afterAction(this, false);
11240         }
11241     },
11242
11243     success : function(response)
11244     {
11245         this.uploadComplete= true;
11246         if (this.haveProgress) {
11247             Roo.MessageBox.hide();
11248         }
11249         
11250         
11251         var result = this.processResponse(response);
11252         if(result === true || result.success){
11253             this.form.afterAction(this, true);
11254             return;
11255         }
11256         if(result.errors){
11257             this.form.markInvalid(result.errors);
11258             this.failureType = Roo.form.Action.SERVER_INVALID;
11259         }
11260         this.form.afterAction(this, false);
11261     },
11262     failure : function(response)
11263     {
11264         this.uploadComplete= true;
11265         if (this.haveProgress) {
11266             Roo.MessageBox.hide();
11267         }
11268         
11269         this.response = response;
11270         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11271         this.form.afterAction(this, false);
11272     },
11273     
11274     handleResponse : function(response){
11275         if(this.form.errorReader){
11276             var rs = this.form.errorReader.read(response);
11277             var errors = [];
11278             if(rs.records){
11279                 for(var i = 0, len = rs.records.length; i < len; i++) {
11280                     var r = rs.records[i];
11281                     errors[i] = r.data;
11282                 }
11283             }
11284             if(errors.length < 1){
11285                 errors = null;
11286             }
11287             return {
11288                 success : rs.success,
11289                 errors : errors
11290             };
11291         }
11292         var ret = false;
11293         try {
11294             ret = Roo.decode(response.responseText);
11295         } catch (e) {
11296             ret = {
11297                 success: false,
11298                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11299                 errors : []
11300             };
11301         }
11302         return ret;
11303         
11304     }
11305 });
11306
11307
11308 Roo.form.Action.Load = function(form, options){
11309     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11310     this.reader = this.form.reader;
11311 };
11312
11313 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11314     type : 'load',
11315
11316     run : function(){
11317         
11318         Roo.Ajax.request(Roo.apply(
11319                 this.createCallback(), {
11320                     method:this.getMethod(),
11321                     url:this.getUrl(false),
11322                     params:this.getParams()
11323         }));
11324     },
11325
11326     success : function(response){
11327         
11328         var result = this.processResponse(response);
11329         if(result === true || !result.success || !result.data){
11330             this.failureType = Roo.form.Action.LOAD_FAILURE;
11331             this.form.afterAction(this, false);
11332             return;
11333         }
11334         this.form.clearInvalid();
11335         this.form.setValues(result.data);
11336         this.form.afterAction(this, true);
11337     },
11338
11339     handleResponse : function(response){
11340         if(this.form.reader){
11341             var rs = this.form.reader.read(response);
11342             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11343             return {
11344                 success : rs.success,
11345                 data : data
11346             };
11347         }
11348         return Roo.decode(response.responseText);
11349     }
11350 });
11351
11352 Roo.form.Action.ACTION_TYPES = {
11353     'load' : Roo.form.Action.Load,
11354     'submit' : Roo.form.Action.Submit
11355 };/*
11356  * - LGPL
11357  *
11358  * form
11359  *
11360  */
11361
11362 /**
11363  * @class Roo.bootstrap.form.Form
11364  * @extends Roo.bootstrap.Component
11365  * @children Roo.bootstrap.Component
11366  * Bootstrap Form class
11367  * @cfg {String} method  GET | POST (default POST)
11368  * @cfg {String} labelAlign top | left (default top)
11369  * @cfg {String} align left  | right - for navbars
11370  * @cfg {Boolean} loadMask load mask when submit (default true)
11371
11372  *
11373  * @constructor
11374  * Create a new Form
11375  * @param {Object} config The config object
11376  */
11377
11378
11379 Roo.bootstrap.form.Form = function(config){
11380     
11381     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11382     
11383     Roo.bootstrap.form.Form.popover.apply();
11384     
11385     this.addEvents({
11386         /**
11387          * @event clientvalidation
11388          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11389          * @param {Form} this
11390          * @param {Boolean} valid true if the form has passed client-side validation
11391          */
11392         clientvalidation: true,
11393         /**
11394          * @event beforeaction
11395          * Fires before any action is performed. Return false to cancel the action.
11396          * @param {Form} this
11397          * @param {Action} action The action to be performed
11398          */
11399         beforeaction: true,
11400         /**
11401          * @event actionfailed
11402          * Fires when an action fails.
11403          * @param {Form} this
11404          * @param {Action} action The action that failed
11405          */
11406         actionfailed : true,
11407         /**
11408          * @event actioncomplete
11409          * Fires when an action is completed.
11410          * @param {Form} this
11411          * @param {Action} action The action that completed
11412          */
11413         actioncomplete : true
11414     });
11415 };
11416
11417 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11418
11419      /**
11420      * @cfg {String} method
11421      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11422      */
11423     method : 'POST',
11424     /**
11425      * @cfg {String} url
11426      * The URL to use for form actions if one isn't supplied in the action options.
11427      */
11428     /**
11429      * @cfg {Boolean} fileUpload
11430      * Set to true if this form is a file upload.
11431      */
11432
11433     /**
11434      * @cfg {Object} baseParams
11435      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11436      */
11437
11438     /**
11439      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11440      */
11441     timeout: 30,
11442     /**
11443      * @cfg {Sting} align (left|right) for navbar forms
11444      */
11445     align : 'left',
11446
11447     // private
11448     activeAction : null,
11449
11450     /**
11451      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11452      * element by passing it or its id or mask the form itself by passing in true.
11453      * @type Mixed
11454      */
11455     waitMsgTarget : false,
11456
11457     loadMask : true,
11458     
11459     /**
11460      * @cfg {Boolean} errorMask (true|false) default false
11461      */
11462     errorMask : false,
11463     
11464     /**
11465      * @cfg {Number} maskOffset Default 100
11466      */
11467     maskOffset : 100,
11468     
11469     /**
11470      * @cfg {Boolean} maskBody
11471      */
11472     maskBody : false,
11473
11474     getAutoCreate : function(){
11475
11476         var cfg = {
11477             tag: 'form',
11478             method : this.method || 'POST',
11479             id : this.id || Roo.id(),
11480             cls : ''
11481         };
11482         if (this.parent().xtype.match(/^Nav/)) {
11483             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11484
11485         }
11486
11487         if (this.labelAlign == 'left' ) {
11488             cfg.cls += ' form-horizontal';
11489         }
11490
11491
11492         return cfg;
11493     },
11494     initEvents : function()
11495     {
11496         this.el.on('submit', this.onSubmit, this);
11497         // this was added as random key presses on the form where triggering form submit.
11498         this.el.on('keypress', function(e) {
11499             if (e.getCharCode() != 13) {
11500                 return true;
11501             }
11502             // we might need to allow it for textareas.. and some other items.
11503             // check e.getTarget().
11504
11505             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11506                 return true;
11507             }
11508
11509             Roo.log("keypress blocked");
11510
11511             e.preventDefault();
11512             return false;
11513         });
11514         
11515     },
11516     // private
11517     onSubmit : function(e){
11518         e.stopEvent();
11519     },
11520
11521      /**
11522      * Returns true if client-side validation on the form is successful.
11523      * @return Boolean
11524      */
11525     isValid : function(){
11526         var items = this.getItems();
11527         var valid = true;
11528         var target = false;
11529         
11530         items.each(function(f){
11531             
11532             if(f.validate()){
11533                 return;
11534             }
11535             
11536             Roo.log('invalid field: ' + f.name);
11537             
11538             valid = false;
11539
11540             if(!target && f.el.isVisible(true)){
11541                 target = f;
11542             }
11543            
11544         });
11545         
11546         if(this.errorMask && !valid){
11547             Roo.bootstrap.form.Form.popover.mask(this, target);
11548         }
11549         
11550         return valid;
11551     },
11552     
11553     /**
11554      * Returns true if any fields in this form have changed since their original load.
11555      * @return Boolean
11556      */
11557     isDirty : function(){
11558         var dirty = false;
11559         var items = this.getItems();
11560         items.each(function(f){
11561            if(f.isDirty()){
11562                dirty = true;
11563                return false;
11564            }
11565            return true;
11566         });
11567         return dirty;
11568     },
11569      /**
11570      * Performs a predefined action (submit or load) or custom actions you define on this form.
11571      * @param {String} actionName The name of the action type
11572      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11573      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11574      * accept other config options):
11575      * <pre>
11576 Property          Type             Description
11577 ----------------  ---------------  ----------------------------------------------------------------------------------
11578 url               String           The url for the action (defaults to the form's url)
11579 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11580 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11581 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11582                                    validate the form on the client (defaults to false)
11583      * </pre>
11584      * @return {BasicForm} this
11585      */
11586     doAction : function(action, options){
11587         if(typeof action == 'string'){
11588             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11589         }
11590         if(this.fireEvent('beforeaction', this, action) !== false){
11591             this.beforeAction(action);
11592             action.run.defer(100, action);
11593         }
11594         return this;
11595     },
11596
11597     // private
11598     beforeAction : function(action){
11599         var o = action.options;
11600         
11601         if(this.loadMask){
11602             
11603             if(this.maskBody){
11604                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11605             } else {
11606                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11607             }
11608         }
11609         // not really supported yet.. ??
11610
11611         //if(this.waitMsgTarget === true){
11612         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11613         //}else if(this.waitMsgTarget){
11614         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11615         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11616         //}else {
11617         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11618        // }
11619
11620     },
11621
11622     // private
11623     afterAction : function(action, success){
11624         this.activeAction = null;
11625         var o = action.options;
11626
11627         if(this.loadMask){
11628             
11629             if(this.maskBody){
11630                 Roo.get(document.body).unmask();
11631             } else {
11632                 this.el.unmask();
11633             }
11634         }
11635         
11636         //if(this.waitMsgTarget === true){
11637 //            this.el.unmask();
11638         //}else if(this.waitMsgTarget){
11639         //    this.waitMsgTarget.unmask();
11640         //}else{
11641         //    Roo.MessageBox.updateProgress(1);
11642         //    Roo.MessageBox.hide();
11643        // }
11644         //
11645         if(success){
11646             if(o.reset){
11647                 this.reset();
11648             }
11649             Roo.callback(o.success, o.scope, [this, action]);
11650             this.fireEvent('actioncomplete', this, action);
11651
11652         }else{
11653
11654             // failure condition..
11655             // we have a scenario where updates need confirming.
11656             // eg. if a locking scenario exists..
11657             // we look for { errors : { needs_confirm : true }} in the response.
11658             if (
11659                 (typeof(action.result) != 'undefined')  &&
11660                 (typeof(action.result.errors) != 'undefined')  &&
11661                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11662            ){
11663                 var _t = this;
11664                 Roo.log("not supported yet");
11665                  /*
11666
11667                 Roo.MessageBox.confirm(
11668                     "Change requires confirmation",
11669                     action.result.errorMsg,
11670                     function(r) {
11671                         if (r != 'yes') {
11672                             return;
11673                         }
11674                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11675                     }
11676
11677                 );
11678                 */
11679
11680
11681                 return;
11682             }
11683
11684             Roo.callback(o.failure, o.scope, [this, action]);
11685             // show an error message if no failed handler is set..
11686             if (!this.hasListener('actionfailed')) {
11687                 Roo.log("need to add dialog support");
11688                 /*
11689                 Roo.MessageBox.alert("Error",
11690                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11691                         action.result.errorMsg :
11692                         "Saving Failed, please check your entries or try again"
11693                 );
11694                 */
11695             }
11696
11697             this.fireEvent('actionfailed', this, action);
11698         }
11699
11700     },
11701     /**
11702      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11703      * @param {String} id The value to search for
11704      * @return Field
11705      */
11706     findField : function(id){
11707         var items = this.getItems();
11708         var field = items.get(id);
11709         if(!field){
11710              items.each(function(f){
11711                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11712                     field = f;
11713                     return false;
11714                 }
11715                 return true;
11716             });
11717         }
11718         return field || null;
11719     },
11720      /**
11721      * Mark fields in this form invalid in bulk.
11722      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11723      * @return {BasicForm} this
11724      */
11725     markInvalid : function(errors){
11726         if(errors instanceof Array){
11727             for(var i = 0, len = errors.length; i < len; i++){
11728                 var fieldError = errors[i];
11729                 var f = this.findField(fieldError.id);
11730                 if(f){
11731                     f.markInvalid(fieldError.msg);
11732                 }
11733             }
11734         }else{
11735             var field, id;
11736             for(id in errors){
11737                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11738                     field.markInvalid(errors[id]);
11739                 }
11740             }
11741         }
11742         //Roo.each(this.childForms || [], function (f) {
11743         //    f.markInvalid(errors);
11744         //});
11745
11746         return this;
11747     },
11748
11749     /**
11750      * Set values for fields in this form in bulk.
11751      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11752      * @return {BasicForm} this
11753      */
11754     setValues : function(values){
11755         if(values instanceof Array){ // array of objects
11756             for(var i = 0, len = values.length; i < len; i++){
11757                 var v = values[i];
11758                 var f = this.findField(v.id);
11759                 if(f){
11760                     f.setValue(v.value);
11761                     if(this.trackResetOnLoad){
11762                         f.originalValue = f.getValue();
11763                     }
11764                 }
11765             }
11766         }else{ // object hash
11767             var field, id;
11768             for(id in values){
11769                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11770
11771                     if (field.setFromData &&
11772                         field.valueField &&
11773                         field.displayField &&
11774                         // combos' with local stores can
11775                         // be queried via setValue()
11776                         // to set their value..
11777                         (field.store && !field.store.isLocal)
11778                         ) {
11779                         // it's a combo
11780                         var sd = { };
11781                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11782                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11783                         field.setFromData(sd);
11784
11785                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11786                         
11787                         field.setFromData(values);
11788                         
11789                     } else {
11790                         field.setValue(values[id]);
11791                     }
11792
11793
11794                     if(this.trackResetOnLoad){
11795                         field.originalValue = field.getValue();
11796                     }
11797                 }
11798             }
11799         }
11800
11801         //Roo.each(this.childForms || [], function (f) {
11802         //    f.setValues(values);
11803         //});
11804
11805         return this;
11806     },
11807
11808     /**
11809      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11810      * they are returned as an array.
11811      * @param {Boolean} asString
11812      * @return {Object}
11813      */
11814     getValues : function(asString){
11815         //if (this.childForms) {
11816             // copy values from the child forms
11817         //    Roo.each(this.childForms, function (f) {
11818         //        this.setValues(f.getValues());
11819         //    }, this);
11820         //}
11821
11822
11823
11824         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11825         if(asString === true){
11826             return fs;
11827         }
11828         return Roo.urlDecode(fs);
11829     },
11830
11831     /**
11832      * Returns the fields in this form as an object with key/value pairs.
11833      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11834      * @return {Object}
11835      */
11836     getFieldValues : function(with_hidden)
11837     {
11838         var items = this.getItems();
11839         var ret = {};
11840         items.each(function(f){
11841             
11842             if (!f.getName()) {
11843                 return;
11844             }
11845             
11846             var v = f.getValue();
11847             
11848             if (f.inputType =='radio') {
11849                 if (typeof(ret[f.getName()]) == 'undefined') {
11850                     ret[f.getName()] = ''; // empty..
11851                 }
11852
11853                 if (!f.el.dom.checked) {
11854                     return;
11855
11856                 }
11857                 v = f.el.dom.value;
11858
11859             }
11860             
11861             if(f.xtype == 'MoneyField'){
11862                 ret[f.currencyName] = f.getCurrency();
11863             }
11864
11865             // not sure if this supported any more..
11866             if ((typeof(v) == 'object') && f.getRawValue) {
11867                 v = f.getRawValue() ; // dates..
11868             }
11869             // combo boxes where name != hiddenName...
11870             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11871                 ret[f.name] = f.getRawValue();
11872             }
11873             ret[f.getName()] = v;
11874         });
11875
11876         return ret;
11877     },
11878
11879     /**
11880      * Clears all invalid messages in this form.
11881      * @return {BasicForm} this
11882      */
11883     clearInvalid : function(){
11884         var items = this.getItems();
11885
11886         items.each(function(f){
11887            f.clearInvalid();
11888         });
11889
11890         return this;
11891     },
11892
11893     /**
11894      * Resets this form.
11895      * @return {BasicForm} this
11896      */
11897     reset : function(){
11898         var items = this.getItems();
11899         items.each(function(f){
11900             f.reset();
11901         });
11902
11903         Roo.each(this.childForms || [], function (f) {
11904             f.reset();
11905         });
11906
11907
11908         return this;
11909     },
11910     
11911     getItems : function()
11912     {
11913         var r=new Roo.util.MixedCollection(false, function(o){
11914             return o.id || (o.id = Roo.id());
11915         });
11916         var iter = function(el) {
11917             if (el.inputEl) {
11918                 r.add(el);
11919             }
11920             if (!el.items) {
11921                 return;
11922             }
11923             Roo.each(el.items,function(e) {
11924                 iter(e);
11925             });
11926         };
11927
11928         iter(this);
11929         return r;
11930     },
11931     
11932     hideFields : function(items)
11933     {
11934         Roo.each(items, function(i){
11935             
11936             var f = this.findField(i);
11937             
11938             if(!f){
11939                 return;
11940             }
11941             
11942             f.hide();
11943             
11944         }, this);
11945     },
11946     
11947     showFields : function(items)
11948     {
11949         Roo.each(items, function(i){
11950             
11951             var f = this.findField(i);
11952             
11953             if(!f){
11954                 return;
11955             }
11956             
11957             f.show();
11958             
11959         }, this);
11960     }
11961
11962 });
11963
11964 Roo.apply(Roo.bootstrap.form.Form, {
11965     
11966     popover : {
11967         
11968         padding : 5,
11969         
11970         isApplied : false,
11971         
11972         isMasked : false,
11973         
11974         form : false,
11975         
11976         target : false,
11977         
11978         toolTip : false,
11979         
11980         intervalID : false,
11981         
11982         maskEl : false,
11983         
11984         apply : function()
11985         {
11986             if(this.isApplied){
11987                 return;
11988             }
11989             
11990             this.maskEl = {
11991                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11992                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11993                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11994                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11995             };
11996             
11997             this.maskEl.top.enableDisplayMode("block");
11998             this.maskEl.left.enableDisplayMode("block");
11999             this.maskEl.bottom.enableDisplayMode("block");
12000             this.maskEl.right.enableDisplayMode("block");
12001             
12002             this.toolTip = new Roo.bootstrap.Tooltip({
12003                 cls : 'roo-form-error-popover',
12004                 alignment : {
12005                     'left' : ['r-l', [-2,0], 'right'],
12006                     'right' : ['l-r', [2,0], 'left'],
12007                     'bottom' : ['tl-bl', [0,2], 'top'],
12008                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12009                 }
12010             });
12011             
12012             this.toolTip.render(Roo.get(document.body));
12013
12014             this.toolTip.el.enableDisplayMode("block");
12015             
12016             Roo.get(document.body).on('click', function(){
12017                 this.unmask();
12018             }, this);
12019             
12020             Roo.get(document.body).on('touchstart', function(){
12021                 this.unmask();
12022             }, this);
12023             
12024             this.isApplied = true
12025         },
12026         
12027         mask : function(form, target)
12028         {
12029             this.form = form;
12030             
12031             this.target = target;
12032             
12033             if(!this.form.errorMask || !target.el){
12034                 return;
12035             }
12036             
12037             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12038             
12039             Roo.log(scrollable);
12040             
12041             var ot = this.target.el.calcOffsetsTo(scrollable);
12042             
12043             var scrollTo = ot[1] - this.form.maskOffset;
12044             
12045             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12046             
12047             scrollable.scrollTo('top', scrollTo);
12048             
12049             var box = this.target.el.getBox();
12050             Roo.log(box);
12051             var zIndex = Roo.bootstrap.Modal.zIndex++;
12052
12053             
12054             this.maskEl.top.setStyle('position', 'absolute');
12055             this.maskEl.top.setStyle('z-index', zIndex);
12056             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12057             this.maskEl.top.setLeft(0);
12058             this.maskEl.top.setTop(0);
12059             this.maskEl.top.show();
12060             
12061             this.maskEl.left.setStyle('position', 'absolute');
12062             this.maskEl.left.setStyle('z-index', zIndex);
12063             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12064             this.maskEl.left.setLeft(0);
12065             this.maskEl.left.setTop(box.y - this.padding);
12066             this.maskEl.left.show();
12067
12068             this.maskEl.bottom.setStyle('position', 'absolute');
12069             this.maskEl.bottom.setStyle('z-index', zIndex);
12070             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12071             this.maskEl.bottom.setLeft(0);
12072             this.maskEl.bottom.setTop(box.bottom + this.padding);
12073             this.maskEl.bottom.show();
12074
12075             this.maskEl.right.setStyle('position', 'absolute');
12076             this.maskEl.right.setStyle('z-index', zIndex);
12077             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12078             this.maskEl.right.setLeft(box.right + this.padding);
12079             this.maskEl.right.setTop(box.y - this.padding);
12080             this.maskEl.right.show();
12081
12082             this.toolTip.bindEl = this.target.el;
12083
12084             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12085
12086             var tip = this.target.blankText;
12087
12088             if(this.target.getValue() !== '' ) {
12089                 
12090                 if (this.target.invalidText.length) {
12091                     tip = this.target.invalidText;
12092                 } else if (this.target.regexText.length){
12093                     tip = this.target.regexText;
12094                 }
12095             }
12096
12097             this.toolTip.show(tip);
12098
12099             this.intervalID = window.setInterval(function() {
12100                 Roo.bootstrap.form.Form.popover.unmask();
12101             }, 10000);
12102
12103             window.onwheel = function(){ return false;};
12104             
12105             (function(){ this.isMasked = true; }).defer(500, this);
12106             
12107         },
12108         
12109         unmask : function()
12110         {
12111             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12112                 return;
12113             }
12114             
12115             this.maskEl.top.setStyle('position', 'absolute');
12116             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12117             this.maskEl.top.hide();
12118
12119             this.maskEl.left.setStyle('position', 'absolute');
12120             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12121             this.maskEl.left.hide();
12122
12123             this.maskEl.bottom.setStyle('position', 'absolute');
12124             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12125             this.maskEl.bottom.hide();
12126
12127             this.maskEl.right.setStyle('position', 'absolute');
12128             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12129             this.maskEl.right.hide();
12130             
12131             this.toolTip.hide();
12132             
12133             this.toolTip.el.hide();
12134             
12135             window.onwheel = function(){ return true;};
12136             
12137             if(this.intervalID){
12138                 window.clearInterval(this.intervalID);
12139                 this.intervalID = false;
12140             }
12141             
12142             this.isMasked = false;
12143             
12144         }
12145         
12146     }
12147     
12148 });
12149
12150 /*
12151  * Based on:
12152  * Ext JS Library 1.1.1
12153  * Copyright(c) 2006-2007, Ext JS, LLC.
12154  *
12155  * Originally Released Under LGPL - original licence link has changed is not relivant.
12156  *
12157  * Fork - LGPL
12158  * <script type="text/javascript">
12159  */
12160 /**
12161  * @class Roo.form.VTypes
12162  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12163  * @static
12164  */
12165 Roo.form.VTypes = function(){
12166     // closure these in so they are only created once.
12167     var alpha = /^[a-zA-Z_]+$/;
12168     var alphanum = /^[a-zA-Z0-9_]+$/;
12169     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12170     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12171
12172     // All these messages and functions are configurable
12173     return {
12174         /**
12175          * The function used to validate email addresses
12176          * @param {String} value The email address
12177          */
12178         'email' : function(v){
12179             return email.test(v);
12180         },
12181         /**
12182          * The error text to display when the email validation function returns false
12183          * @type String
12184          */
12185         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12186         /**
12187          * The keystroke filter mask to be applied on email input
12188          * @type RegExp
12189          */
12190         'emailMask' : /[a-z0-9_\.\-@]/i,
12191
12192         /**
12193          * The function used to validate URLs
12194          * @param {String} value The URL
12195          */
12196         'url' : function(v){
12197             return url.test(v);
12198         },
12199         /**
12200          * The error text to display when the url validation function returns false
12201          * @type String
12202          */
12203         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12204         
12205         /**
12206          * The function used to validate alpha values
12207          * @param {String} value The value
12208          */
12209         'alpha' : function(v){
12210             return alpha.test(v);
12211         },
12212         /**
12213          * The error text to display when the alpha validation function returns false
12214          * @type String
12215          */
12216         'alphaText' : 'This field should only contain letters and _',
12217         /**
12218          * The keystroke filter mask to be applied on alpha input
12219          * @type RegExp
12220          */
12221         'alphaMask' : /[a-z_]/i,
12222
12223         /**
12224          * The function used to validate alphanumeric values
12225          * @param {String} value The value
12226          */
12227         'alphanum' : function(v){
12228             return alphanum.test(v);
12229         },
12230         /**
12231          * The error text to display when the alphanumeric validation function returns false
12232          * @type String
12233          */
12234         'alphanumText' : 'This field should only contain letters, numbers and _',
12235         /**
12236          * The keystroke filter mask to be applied on alphanumeric input
12237          * @type RegExp
12238          */
12239         'alphanumMask' : /[a-z0-9_]/i
12240     };
12241 }();/*
12242  * - LGPL
12243  *
12244  * Input
12245  * 
12246  */
12247
12248 /**
12249  * @class Roo.bootstrap.form.Input
12250  * @extends Roo.bootstrap.Component
12251  * Bootstrap Input class
12252  * @cfg {Boolean} disabled is it disabled
12253  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12254  * @cfg {String} name name of the input
12255  * @cfg {string} fieldLabel - the label associated
12256  * @cfg {string} placeholder - placeholder to put in text.
12257  * @cfg {string} before - input group add on before
12258  * @cfg {string} after - input group add on after
12259  * @cfg {string} size - (lg|sm) or leave empty..
12260  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12261  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12262  * @cfg {Number} md colspan out of 12 for computer-sized screens
12263  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12264  * @cfg {string} value default value of the input
12265  * @cfg {Number} labelWidth set the width of label 
12266  * @cfg {Number} labellg set the width of label (1-12)
12267  * @cfg {Number} labelmd set the width of label (1-12)
12268  * @cfg {Number} labelsm set the width of label (1-12)
12269  * @cfg {Number} labelxs set the width of label (1-12)
12270  * @cfg {String} labelAlign (top|left)
12271  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12272  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12273  * @cfg {String} indicatorpos (left|right) default left
12274  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12275  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12276  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12277  * @cfg {Roo.bootstrap.Button} before Button to show before
12278  * @cfg {Roo.bootstrap.Button} afterButton to show before
12279  * @cfg {String} align (left|center|right) Default left
12280  * @cfg {Boolean} forceFeedback (true|false) Default false
12281  * 
12282  * @constructor
12283  * Create a new Input
12284  * @param {Object} config The config object
12285  */
12286
12287 Roo.bootstrap.form.Input = function(config){
12288     
12289     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12290     
12291     this.addEvents({
12292         /**
12293          * @event focus
12294          * Fires when this field receives input focus.
12295          * @param {Roo.form.Field} this
12296          */
12297         focus : true,
12298         /**
12299          * @event blur
12300          * Fires when this field loses input focus.
12301          * @param {Roo.form.Field} this
12302          */
12303         blur : true,
12304         /**
12305          * @event specialkey
12306          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12307          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12308          * @param {Roo.form.Field} this
12309          * @param {Roo.EventObject} e The event object
12310          */
12311         specialkey : true,
12312         /**
12313          * @event change
12314          * Fires just before the field blurs if the field value has changed.
12315          * @param {Roo.form.Field} this
12316          * @param {Mixed} newValue The new value
12317          * @param {Mixed} oldValue The original value
12318          */
12319         change : true,
12320         /**
12321          * @event invalid
12322          * Fires after the field has been marked as invalid.
12323          * @param {Roo.form.Field} this
12324          * @param {String} msg The validation message
12325          */
12326         invalid : true,
12327         /**
12328          * @event valid
12329          * Fires after the field has been validated with no errors.
12330          * @param {Roo.form.Field} this
12331          */
12332         valid : true,
12333          /**
12334          * @event keyup
12335          * Fires after the key up
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject}  e The event Object
12338          */
12339         keyup : true,
12340         /**
12341          * @event paste
12342          * Fires after the user pastes into input
12343          * @param {Roo.form.Field} this
12344          * @param {Roo.EventObject}  e The event Object
12345          */
12346         paste : true
12347     });
12348 };
12349
12350 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12351      /**
12352      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12353       automatic validation (defaults to "keyup").
12354      */
12355     validationEvent : "keyup",
12356      /**
12357      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12358      */
12359     validateOnBlur : true,
12360     /**
12361      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12362      */
12363     validationDelay : 250,
12364      /**
12365      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12366      */
12367     focusClass : "x-form-focus",  // not needed???
12368     
12369        
12370     /**
12371      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12372      */
12373     invalidClass : "has-warning",
12374     
12375     /**
12376      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12377      */
12378     validClass : "has-success",
12379     
12380     /**
12381      * @cfg {Boolean} hasFeedback (true|false) default true
12382      */
12383     hasFeedback : true,
12384     
12385     /**
12386      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12387      */
12388     invalidFeedbackClass : "glyphicon-warning-sign",
12389     
12390     /**
12391      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12392      */
12393     validFeedbackClass : "glyphicon-ok",
12394     
12395     /**
12396      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12397      */
12398     selectOnFocus : false,
12399     
12400      /**
12401      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12402      */
12403     maskRe : null,
12404        /**
12405      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12406      */
12407     vtype : null,
12408     
12409       /**
12410      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12411      */
12412     disableKeyFilter : false,
12413     
12414        /**
12415      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12416      */
12417     disabled : false,
12418      /**
12419      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12420      */
12421     allowBlank : true,
12422     /**
12423      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12424      */
12425     blankText : "Please complete this mandatory field",
12426     
12427      /**
12428      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12429      */
12430     minLength : 0,
12431     /**
12432      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12433      */
12434     maxLength : Number.MAX_VALUE,
12435     /**
12436      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12437      */
12438     minLengthText : "The minimum length for this field is {0}",
12439     /**
12440      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12441      */
12442     maxLengthText : "The maximum length for this field is {0}",
12443   
12444     
12445     /**
12446      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12447      * If available, this function will be called only after the basic validators all return true, and will be passed the
12448      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12449      */
12450     validator : null,
12451     /**
12452      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12453      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12454      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12455      */
12456     regex : null,
12457     /**
12458      * @cfg {String} regexText -- Depricated - use Invalid Text
12459      */
12460     regexText : "",
12461     
12462     /**
12463      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12464      */
12465     invalidText : "",
12466     
12467     
12468     
12469     autocomplete: false,
12470     
12471     
12472     fieldLabel : '',
12473     inputType : 'text',
12474     
12475     name : false,
12476     placeholder: false,
12477     before : false,
12478     after : false,
12479     size : false,
12480     hasFocus : false,
12481     preventMark: false,
12482     isFormField : true,
12483     value : '',
12484     labelWidth : 2,
12485     labelAlign : false,
12486     readOnly : false,
12487     align : false,
12488     formatedValue : false,
12489     forceFeedback : false,
12490     
12491     indicatorpos : 'left',
12492     
12493     labellg : 0,
12494     labelmd : 0,
12495     labelsm : 0,
12496     labelxs : 0,
12497     
12498     capture : '',
12499     accept : '',
12500     
12501     parentLabelAlign : function()
12502     {
12503         var parent = this;
12504         while (parent.parent()) {
12505             parent = parent.parent();
12506             if (typeof(parent.labelAlign) !='undefined') {
12507                 return parent.labelAlign;
12508             }
12509         }
12510         return 'left';
12511         
12512     },
12513     
12514     getAutoCreate : function()
12515     {
12516         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12517         
12518         var id = Roo.id();
12519         
12520         var cfg = {};
12521         
12522         if(this.inputType != 'hidden'){
12523             cfg.cls = 'form-group' //input-group
12524         }
12525         
12526         var input =  {
12527             tag: 'input',
12528             id : id,
12529             type : this.inputType,
12530             value : this.value,
12531             cls : 'form-control',
12532             placeholder : this.placeholder || '',
12533             autocomplete : this.autocomplete || 'new-password'
12534         };
12535         if (this.inputType == 'file') {
12536             input.style = 'overflow:hidden'; // why not in CSS?
12537         }
12538         
12539         if(this.capture.length){
12540             input.capture = this.capture;
12541         }
12542         
12543         if(this.accept.length){
12544             input.accept = this.accept + "/*";
12545         }
12546         
12547         if(this.align){
12548             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12549         }
12550         
12551         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12552             input.maxLength = this.maxLength;
12553         }
12554         
12555         if (this.disabled) {
12556             input.disabled=true;
12557         }
12558         
12559         if (this.readOnly) {
12560             input.readonly=true;
12561         }
12562         
12563         if (this.name) {
12564             input.name = this.name;
12565         }
12566         
12567         if (this.size) {
12568             input.cls += ' input-' + this.size;
12569         }
12570         
12571         var settings=this;
12572         ['xs','sm','md','lg'].map(function(size){
12573             if (settings[size]) {
12574                 cfg.cls += ' col-' + size + '-' + settings[size];
12575             }
12576         });
12577         
12578         var inputblock = input;
12579         
12580         var feedback = {
12581             tag: 'span',
12582             cls: 'glyphicon form-control-feedback'
12583         };
12584             
12585         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12586             
12587             inputblock = {
12588                 cls : 'has-feedback',
12589                 cn :  [
12590                     input,
12591                     feedback
12592                 ] 
12593             };  
12594         }
12595         
12596         if (this.before || this.after) {
12597             
12598             inputblock = {
12599                 cls : 'input-group',
12600                 cn :  [] 
12601             };
12602             
12603             if (this.before && typeof(this.before) == 'string') {
12604                 
12605                 inputblock.cn.push({
12606                     tag :'span',
12607                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12608                     html : this.before
12609                 });
12610             }
12611             if (this.before && typeof(this.before) == 'object') {
12612                 this.before = Roo.factory(this.before);
12613                 
12614                 inputblock.cn.push({
12615                     tag :'span',
12616                     cls : 'roo-input-before input-group-prepend   input-group-' +
12617                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12618                 });
12619             }
12620             
12621             inputblock.cn.push(input);
12622             
12623             if (this.after && typeof(this.after) == 'string') {
12624                 inputblock.cn.push({
12625                     tag :'span',
12626                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12627                     html : this.after
12628                 });
12629             }
12630             if (this.after && typeof(this.after) == 'object') {
12631                 this.after = Roo.factory(this.after);
12632                 
12633                 inputblock.cn.push({
12634                     tag :'span',
12635                     cls : 'roo-input-after input-group-append  input-group-' +
12636                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12637                 });
12638             }
12639             
12640             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12641                 inputblock.cls += ' has-feedback';
12642                 inputblock.cn.push(feedback);
12643             }
12644         };
12645         var indicator = {
12646             tag : 'i',
12647             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12648             tooltip : 'This field is required'
12649         };
12650         if (this.allowBlank ) {
12651             indicator.style = this.allowBlank ? ' display:none' : '';
12652         }
12653         if (align ==='left' && this.fieldLabel.length) {
12654             
12655             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12656             
12657             cfg.cn = [
12658                 indicator,
12659                 {
12660                     tag: 'label',
12661                     'for' :  id,
12662                     cls : 'control-label col-form-label',
12663                     html : this.fieldLabel
12664
12665                 },
12666                 {
12667                     cls : "", 
12668                     cn: [
12669                         inputblock
12670                     ]
12671                 }
12672             ];
12673             
12674             var labelCfg = cfg.cn[1];
12675             var contentCfg = cfg.cn[2];
12676             
12677             if(this.indicatorpos == 'right'){
12678                 cfg.cn = [
12679                     {
12680                         tag: 'label',
12681                         'for' :  id,
12682                         cls : 'control-label col-form-label',
12683                         cn : [
12684                             {
12685                                 tag : 'span',
12686                                 html : this.fieldLabel
12687                             },
12688                             indicator
12689                         ]
12690                     },
12691                     {
12692                         cls : "",
12693                         cn: [
12694                             inputblock
12695                         ]
12696                     }
12697
12698                 ];
12699                 
12700                 labelCfg = cfg.cn[0];
12701                 contentCfg = cfg.cn[1];
12702             
12703             }
12704             
12705             if(this.labelWidth > 12){
12706                 labelCfg.style = "width: " + this.labelWidth + 'px';
12707             }
12708             
12709             if(this.labelWidth < 13 && this.labelmd == 0){
12710                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12711             }
12712             
12713             if(this.labellg > 0){
12714                 labelCfg.cls += ' col-lg-' + this.labellg;
12715                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12716             }
12717             
12718             if(this.labelmd > 0){
12719                 labelCfg.cls += ' col-md-' + this.labelmd;
12720                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12721             }
12722             
12723             if(this.labelsm > 0){
12724                 labelCfg.cls += ' col-sm-' + this.labelsm;
12725                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12726             }
12727             
12728             if(this.labelxs > 0){
12729                 labelCfg.cls += ' col-xs-' + this.labelxs;
12730                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12731             }
12732             
12733             
12734         } else if ( this.fieldLabel.length) {
12735                 
12736             
12737             
12738             cfg.cn = [
12739                 {
12740                     tag : 'i',
12741                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12742                     tooltip : 'This field is required',
12743                     style : this.allowBlank ? ' display:none' : '' 
12744                 },
12745                 {
12746                     tag: 'label',
12747                    //cls : 'input-group-addon',
12748                     html : this.fieldLabel
12749
12750                 },
12751
12752                inputblock
12753
12754            ];
12755            
12756            if(this.indicatorpos == 'right'){
12757        
12758                 cfg.cn = [
12759                     {
12760                         tag: 'label',
12761                        //cls : 'input-group-addon',
12762                         html : this.fieldLabel
12763
12764                     },
12765                     {
12766                         tag : 'i',
12767                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12768                         tooltip : 'This field is required',
12769                         style : this.allowBlank ? ' display:none' : '' 
12770                     },
12771
12772                    inputblock
12773
12774                ];
12775
12776             }
12777
12778         } else {
12779             
12780             cfg.cn = [
12781
12782                     inputblock
12783
12784             ];
12785                 
12786                 
12787         };
12788         
12789         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12790            cfg.cls += ' navbar-form';
12791         }
12792         
12793         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12794             // on BS4 we do this only if not form 
12795             cfg.cls += ' navbar-form';
12796             cfg.tag = 'li';
12797         }
12798         
12799         return cfg;
12800         
12801     },
12802     /**
12803      * return the real input element.
12804      */
12805     inputEl: function ()
12806     {
12807         return this.el.select('input.form-control',true).first();
12808     },
12809     
12810     tooltipEl : function()
12811     {
12812         return this.inputEl();
12813     },
12814     
12815     indicatorEl : function()
12816     {
12817         if (Roo.bootstrap.version == 4) {
12818             return false; // not enabled in v4 yet.
12819         }
12820         
12821         var indicator = this.el.select('i.roo-required-indicator',true).first();
12822         
12823         if(!indicator){
12824             return false;
12825         }
12826         
12827         return indicator;
12828         
12829     },
12830     
12831     setDisabled : function(v)
12832     {
12833         var i  = this.inputEl().dom;
12834         if (!v) {
12835             i.removeAttribute('disabled');
12836             return;
12837             
12838         }
12839         i.setAttribute('disabled','true');
12840     },
12841     initEvents : function()
12842     {
12843           
12844         this.inputEl().on("keydown" , this.fireKey,  this);
12845         this.inputEl().on("focus", this.onFocus,  this);
12846         this.inputEl().on("blur", this.onBlur,  this);
12847         
12848         this.inputEl().relayEvent('keyup', this);
12849         this.inputEl().relayEvent('paste', this);
12850         
12851         this.indicator = this.indicatorEl();
12852         
12853         if(this.indicator){
12854             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12855         }
12856  
12857         // reference to original value for reset
12858         this.originalValue = this.getValue();
12859         //Roo.form.TextField.superclass.initEvents.call(this);
12860         if(this.validationEvent == 'keyup'){
12861             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12862             this.inputEl().on('keyup', this.filterValidation, this);
12863         }
12864         else if(this.validationEvent !== false){
12865             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12866         }
12867         
12868         if(this.selectOnFocus){
12869             this.on("focus", this.preFocus, this);
12870             
12871         }
12872         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12873             this.inputEl().on("keypress", this.filterKeys, this);
12874         } else {
12875             this.inputEl().relayEvent('keypress', this);
12876         }
12877        /* if(this.grow){
12878             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12879             this.el.on("click", this.autoSize,  this);
12880         }
12881         */
12882         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12883             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12884         }
12885         
12886         if (typeof(this.before) == 'object') {
12887             this.before.render(this.el.select('.roo-input-before',true).first());
12888         }
12889         if (typeof(this.after) == 'object') {
12890             this.after.render(this.el.select('.roo-input-after',true).first());
12891         }
12892         
12893         this.inputEl().on('change', this.onChange, this);
12894         
12895     },
12896     filterValidation : function(e){
12897         if(!e.isNavKeyPress()){
12898             this.validationTask.delay(this.validationDelay);
12899         }
12900     },
12901      /**
12902      * Validates the field value
12903      * @return {Boolean} True if the value is valid, else false
12904      */
12905     validate : function(){
12906         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12907         if(this.disabled || this.validateValue(this.getRawValue())){
12908             this.markValid();
12909             return true;
12910         }
12911         
12912         this.markInvalid();
12913         return false;
12914     },
12915     
12916     
12917     /**
12918      * Validates a value according to the field's validation rules and marks the field as invalid
12919      * if the validation fails
12920      * @param {Mixed} value The value to validate
12921      * @return {Boolean} True if the value is valid, else false
12922      */
12923     validateValue : function(value)
12924     {
12925         if(this.getVisibilityEl().hasClass('hidden')){
12926             return true;
12927         }
12928         
12929         if(value.length < 1)  { // if it's blank
12930             if(this.allowBlank){
12931                 return true;
12932             }
12933             return false;
12934         }
12935         
12936         if(value.length < this.minLength){
12937             return false;
12938         }
12939         if(value.length > this.maxLength){
12940             return false;
12941         }
12942         if(this.vtype){
12943             var vt = Roo.form.VTypes;
12944             if(!vt[this.vtype](value, this)){
12945                 return false;
12946             }
12947         }
12948         if(typeof this.validator == "function"){
12949             var msg = this.validator(value);
12950             if(msg !== true){
12951                 return false;
12952             }
12953             if (typeof(msg) == 'string') {
12954                 this.invalidText = msg;
12955             }
12956         }
12957         
12958         if(this.regex && !this.regex.test(value)){
12959             return false;
12960         }
12961         
12962         return true;
12963     },
12964     
12965      // private
12966     fireKey : function(e){
12967         //Roo.log('field ' + e.getKey());
12968         if(e.isNavKeyPress()){
12969             this.fireEvent("specialkey", this, e);
12970         }
12971     },
12972     focus : function (selectText){
12973         if(this.rendered){
12974             this.inputEl().focus();
12975             if(selectText === true){
12976                 this.inputEl().dom.select();
12977             }
12978         }
12979         return this;
12980     } ,
12981     
12982     onFocus : function(){
12983         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12984            // this.el.addClass(this.focusClass);
12985         }
12986         if(!this.hasFocus){
12987             this.hasFocus = true;
12988             this.startValue = this.getValue();
12989             this.fireEvent("focus", this);
12990         }
12991     },
12992     
12993     beforeBlur : Roo.emptyFn,
12994
12995     
12996     // private
12997     onBlur : function(){
12998         this.beforeBlur();
12999         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13000             //this.el.removeClass(this.focusClass);
13001         }
13002         this.hasFocus = false;
13003         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13004             this.validate();
13005         }
13006         var v = this.getValue();
13007         if(String(v) !== String(this.startValue)){
13008             this.fireEvent('change', this, v, this.startValue);
13009         }
13010         this.fireEvent("blur", this);
13011     },
13012     
13013     onChange : function(e)
13014     {
13015         var v = this.getValue();
13016         if(String(v) !== String(this.startValue)){
13017             this.fireEvent('change', this, v, this.startValue);
13018         }
13019         
13020     },
13021     
13022     /**
13023      * Resets the current field value to the originally loaded value and clears any validation messages
13024      */
13025     reset : function(){
13026         this.setValue(this.originalValue);
13027         this.validate();
13028     },
13029      /**
13030      * Returns the name of the field
13031      * @return {Mixed} name The name field
13032      */
13033     getName: function(){
13034         return this.name;
13035     },
13036      /**
13037      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13038      * @return {Mixed} value The field value
13039      */
13040     getValue : function(){
13041         
13042         var v = this.inputEl().getValue();
13043         
13044         return v;
13045     },
13046     /**
13047      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13048      * @return {Mixed} value The field value
13049      */
13050     getRawValue : function(){
13051         var v = this.inputEl().getValue();
13052         
13053         return v;
13054     },
13055     
13056     /**
13057      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13058      * @param {Mixed} value The value to set
13059      */
13060     setRawValue : function(v){
13061         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13062     },
13063     
13064     selectText : function(start, end){
13065         var v = this.getRawValue();
13066         if(v.length > 0){
13067             start = start === undefined ? 0 : start;
13068             end = end === undefined ? v.length : end;
13069             var d = this.inputEl().dom;
13070             if(d.setSelectionRange){
13071                 d.setSelectionRange(start, end);
13072             }else if(d.createTextRange){
13073                 var range = d.createTextRange();
13074                 range.moveStart("character", start);
13075                 range.moveEnd("character", v.length-end);
13076                 range.select();
13077             }
13078         }
13079     },
13080     
13081     /**
13082      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13083      * @param {Mixed} value The value to set
13084      */
13085     setValue : function(v){
13086         this.value = v;
13087         if(this.rendered){
13088             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13089             this.validate();
13090         }
13091     },
13092     
13093     /*
13094     processValue : function(value){
13095         if(this.stripCharsRe){
13096             var newValue = value.replace(this.stripCharsRe, '');
13097             if(newValue !== value){
13098                 this.setRawValue(newValue);
13099                 return newValue;
13100             }
13101         }
13102         return value;
13103     },
13104   */
13105     preFocus : function(){
13106         
13107         if(this.selectOnFocus){
13108             this.inputEl().dom.select();
13109         }
13110     },
13111     filterKeys : function(e){
13112         var k = e.getKey();
13113         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13114             return;
13115         }
13116         var c = e.getCharCode(), cc = String.fromCharCode(c);
13117         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13118             return;
13119         }
13120         if(!this.maskRe.test(cc)){
13121             e.stopEvent();
13122         }
13123     },
13124      /**
13125      * Clear any invalid styles/messages for this field
13126      */
13127     clearInvalid : function(){
13128         
13129         if(!this.el || this.preventMark){ // not rendered
13130             return;
13131         }
13132         
13133         
13134         this.el.removeClass([this.invalidClass, 'is-invalid']);
13135         
13136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13137             
13138             var feedback = this.el.select('.form-control-feedback', true).first();
13139             
13140             if(feedback){
13141                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13142             }
13143             
13144         }
13145         
13146         if(this.indicator){
13147             this.indicator.removeClass('visible');
13148             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13149         }
13150         
13151         this.fireEvent('valid', this);
13152     },
13153     
13154      /**
13155      * Mark this field as valid
13156      */
13157     markValid : function()
13158     {
13159         if(!this.el  || this.preventMark){ // not rendered...
13160             return;
13161         }
13162         
13163         this.el.removeClass([this.invalidClass, this.validClass]);
13164         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13165
13166         var feedback = this.el.select('.form-control-feedback', true).first();
13167             
13168         if(feedback){
13169             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13170         }
13171         
13172         if(this.indicator){
13173             this.indicator.removeClass('visible');
13174             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13175         }
13176         
13177         if(this.disabled){
13178             return;
13179         }
13180         
13181            
13182         if(this.allowBlank && !this.getRawValue().length){
13183             return;
13184         }
13185         if (Roo.bootstrap.version == 3) {
13186             this.el.addClass(this.validClass);
13187         } else {
13188             this.inputEl().addClass('is-valid');
13189         }
13190
13191         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13192             
13193             var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195             if(feedback){
13196                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13197                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13198             }
13199             
13200         }
13201         
13202         this.fireEvent('valid', this);
13203     },
13204     
13205      /**
13206      * Mark this field as invalid
13207      * @param {String} msg The validation message
13208      */
13209     markInvalid : function(msg)
13210     {
13211         if(!this.el  || this.preventMark){ // not rendered
13212             return;
13213         }
13214         
13215         this.el.removeClass([this.invalidClass, this.validClass]);
13216         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13217         
13218         var feedback = this.el.select('.form-control-feedback', true).first();
13219             
13220         if(feedback){
13221             this.el.select('.form-control-feedback', true).first().removeClass(
13222                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13223         }
13224
13225         if(this.disabled){
13226             return;
13227         }
13228         
13229         if(this.allowBlank && !this.getRawValue().length){
13230             return;
13231         }
13232         
13233         if(this.indicator){
13234             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13235             this.indicator.addClass('visible');
13236         }
13237         if (Roo.bootstrap.version == 3) {
13238             this.el.addClass(this.invalidClass);
13239         } else {
13240             this.inputEl().addClass('is-invalid');
13241         }
13242         
13243         
13244         
13245         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13246             
13247             var feedback = this.el.select('.form-control-feedback', true).first();
13248             
13249             if(feedback){
13250                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13251                 
13252                 if(this.getValue().length || this.forceFeedback){
13253                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13254                 }
13255                 
13256             }
13257             
13258         }
13259         
13260         this.fireEvent('invalid', this, msg);
13261     },
13262     // private
13263     SafariOnKeyDown : function(event)
13264     {
13265         // this is a workaround for a password hang bug on chrome/ webkit.
13266         if (this.inputEl().dom.type != 'password') {
13267             return;
13268         }
13269         
13270         var isSelectAll = false;
13271         
13272         if(this.inputEl().dom.selectionEnd > 0){
13273             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13274         }
13275         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13276             event.preventDefault();
13277             this.setValue('');
13278             return;
13279         }
13280         
13281         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13282             
13283             event.preventDefault();
13284             // this is very hacky as keydown always get's upper case.
13285             //
13286             var cc = String.fromCharCode(event.getCharCode());
13287             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13288             
13289         }
13290     },
13291     adjustWidth : function(tag, w){
13292         tag = tag.toLowerCase();
13293         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13294             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13295                 if(tag == 'input'){
13296                     return w + 2;
13297                 }
13298                 if(tag == 'textarea'){
13299                     return w-2;
13300                 }
13301             }else if(Roo.isOpera){
13302                 if(tag == 'input'){
13303                     return w + 2;
13304                 }
13305                 if(tag == 'textarea'){
13306                     return w-2;
13307                 }
13308             }
13309         }
13310         return w;
13311     },
13312     
13313     setFieldLabel : function(v)
13314     {
13315         if(!this.rendered){
13316             return;
13317         }
13318         
13319         if(this.indicatorEl()){
13320             var ar = this.el.select('label > span',true);
13321             
13322             if (ar.elements.length) {
13323                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13324                 this.fieldLabel = v;
13325                 return;
13326             }
13327             
13328             var br = this.el.select('label',true);
13329             
13330             if(br.elements.length) {
13331                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13332                 this.fieldLabel = v;
13333                 return;
13334             }
13335             
13336             Roo.log('Cannot Found any of label > span || label in input');
13337             return;
13338         }
13339         
13340         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13341         this.fieldLabel = v;
13342         
13343         
13344     }
13345 });
13346
13347  
13348 /*
13349  * - LGPL
13350  *
13351  * Input
13352  * 
13353  */
13354
13355 /**
13356  * @class Roo.bootstrap.form.TextArea
13357  * @extends Roo.bootstrap.form.Input
13358  * Bootstrap TextArea class
13359  * @cfg {Number} cols Specifies the visible width of a text area
13360  * @cfg {Number} rows Specifies the visible number of lines in a text area
13361  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13362  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13363  * @cfg {string} html text
13364  * 
13365  * @constructor
13366  * Create a new TextArea
13367  * @param {Object} config The config object
13368  */
13369
13370 Roo.bootstrap.form.TextArea = function(config){
13371     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13372    
13373 };
13374
13375 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13376      
13377     cols : false,
13378     rows : 5,
13379     readOnly : false,
13380     warp : 'soft',
13381     resize : false,
13382     value: false,
13383     html: false,
13384     
13385     getAutoCreate : function(){
13386         
13387         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13388         
13389         var id = Roo.id();
13390         
13391         var cfg = {};
13392         
13393         if(this.inputType != 'hidden'){
13394             cfg.cls = 'form-group' //input-group
13395         }
13396         
13397         var input =  {
13398             tag: 'textarea',
13399             id : id,
13400             warp : this.warp,
13401             rows : this.rows,
13402             value : this.value || '',
13403             html: this.html || '',
13404             cls : 'form-control',
13405             placeholder : this.placeholder || '' 
13406             
13407         };
13408         
13409         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13410             input.maxLength = this.maxLength;
13411         }
13412         
13413         if(this.resize){
13414             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13415         }
13416         
13417         if(this.cols){
13418             input.cols = this.cols;
13419         }
13420         
13421         if (this.readOnly) {
13422             input.readonly = true;
13423         }
13424         
13425         if (this.name) {
13426             input.name = this.name;
13427         }
13428         
13429         if (this.size) {
13430             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13431         }
13432         
13433         var settings=this;
13434         ['xs','sm','md','lg'].map(function(size){
13435             if (settings[size]) {
13436                 cfg.cls += ' col-' + size + '-' + settings[size];
13437             }
13438         });
13439         
13440         var inputblock = input;
13441         
13442         if(this.hasFeedback && !this.allowBlank){
13443             
13444             var feedback = {
13445                 tag: 'span',
13446                 cls: 'glyphicon form-control-feedback'
13447             };
13448
13449             inputblock = {
13450                 cls : 'has-feedback',
13451                 cn :  [
13452                     input,
13453                     feedback
13454                 ] 
13455             };  
13456         }
13457         
13458         
13459         if (this.before || this.after) {
13460             
13461             inputblock = {
13462                 cls : 'input-group',
13463                 cn :  [] 
13464             };
13465             if (this.before) {
13466                 inputblock.cn.push({
13467                     tag :'span',
13468                     cls : 'input-group-addon',
13469                     html : this.before
13470                 });
13471             }
13472             
13473             inputblock.cn.push(input);
13474             
13475             if(this.hasFeedback && !this.allowBlank){
13476                 inputblock.cls += ' has-feedback';
13477                 inputblock.cn.push(feedback);
13478             }
13479             
13480             if (this.after) {
13481                 inputblock.cn.push({
13482                     tag :'span',
13483                     cls : 'input-group-addon',
13484                     html : this.after
13485                 });
13486             }
13487             
13488         }
13489         
13490         if (align ==='left' && this.fieldLabel.length) {
13491             cfg.cn = [
13492                 {
13493                     tag: 'label',
13494                     'for' :  id,
13495                     cls : 'control-label',
13496                     html : this.fieldLabel
13497                 },
13498                 {
13499                     cls : "",
13500                     cn: [
13501                         inputblock
13502                     ]
13503                 }
13504
13505             ];
13506             
13507             if(this.labelWidth > 12){
13508                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13509             }
13510
13511             if(this.labelWidth < 13 && this.labelmd == 0){
13512                 this.labelmd = this.labelWidth;
13513             }
13514
13515             if(this.labellg > 0){
13516                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13517                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13518             }
13519
13520             if(this.labelmd > 0){
13521                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13522                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13523             }
13524
13525             if(this.labelsm > 0){
13526                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13527                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13528             }
13529
13530             if(this.labelxs > 0){
13531                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13532                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13533             }
13534             
13535         } else if ( this.fieldLabel.length) {
13536             cfg.cn = [
13537
13538                {
13539                    tag: 'label',
13540                    //cls : 'input-group-addon',
13541                    html : this.fieldLabel
13542
13543                },
13544
13545                inputblock
13546
13547            ];
13548
13549         } else {
13550
13551             cfg.cn = [
13552
13553                 inputblock
13554
13555             ];
13556                 
13557         }
13558         
13559         if (this.disabled) {
13560             input.disabled=true;
13561         }
13562         
13563         return cfg;
13564         
13565     },
13566     /**
13567      * return the real textarea element.
13568      */
13569     inputEl: function ()
13570     {
13571         return this.el.select('textarea.form-control',true).first();
13572     },
13573     
13574     /**
13575      * Clear any invalid styles/messages for this field
13576      */
13577     clearInvalid : function()
13578     {
13579         
13580         if(!this.el || this.preventMark){ // not rendered
13581             return;
13582         }
13583         
13584         var label = this.el.select('label', true).first();
13585         var icon = this.el.select('i.fa-star', true).first();
13586         
13587         if(label && icon){
13588             icon.remove();
13589         }
13590         this.el.removeClass( this.validClass);
13591         this.inputEl().removeClass('is-invalid');
13592          
13593         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13594             
13595             var feedback = this.el.select('.form-control-feedback', true).first();
13596             
13597             if(feedback){
13598                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13599             }
13600             
13601         }
13602         
13603         this.fireEvent('valid', this);
13604     },
13605     
13606      /**
13607      * Mark this field as valid
13608      */
13609     markValid : function()
13610     {
13611         if(!this.el  || this.preventMark){ // not rendered
13612             return;
13613         }
13614         
13615         this.el.removeClass([this.invalidClass, this.validClass]);
13616         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13617         
13618         var feedback = this.el.select('.form-control-feedback', true).first();
13619             
13620         if(feedback){
13621             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13622         }
13623
13624         if(this.disabled || this.allowBlank){
13625             return;
13626         }
13627         
13628         var label = this.el.select('label', true).first();
13629         var icon = this.el.select('i.fa-star', true).first();
13630         
13631         if(label && icon){
13632             icon.remove();
13633         }
13634         if (Roo.bootstrap.version == 3) {
13635             this.el.addClass(this.validClass);
13636         } else {
13637             this.inputEl().addClass('is-valid');
13638         }
13639         
13640         
13641         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13642             
13643             var feedback = this.el.select('.form-control-feedback', true).first();
13644             
13645             if(feedback){
13646                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13647                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13648             }
13649             
13650         }
13651         
13652         this.fireEvent('valid', this);
13653     },
13654     
13655      /**
13656      * Mark this field as invalid
13657      * @param {String} msg The validation message
13658      */
13659     markInvalid : function(msg)
13660     {
13661         if(!this.el  || this.preventMark){ // not rendered
13662             return;
13663         }
13664         
13665         this.el.removeClass([this.invalidClass, this.validClass]);
13666         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13667         
13668         var feedback = this.el.select('.form-control-feedback', true).first();
13669             
13670         if(feedback){
13671             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13672         }
13673
13674         if(this.disabled || this.allowBlank){
13675             return;
13676         }
13677         
13678         var label = this.el.select('label', true).first();
13679         var icon = this.el.select('i.fa-star', true).first();
13680         
13681         if(!this.getValue().length && label && !icon){
13682             this.el.createChild({
13683                 tag : 'i',
13684                 cls : 'text-danger fa fa-lg fa-star',
13685                 tooltip : 'This field is required',
13686                 style : 'margin-right:5px;'
13687             }, label, true);
13688         }
13689         
13690         if (Roo.bootstrap.version == 3) {
13691             this.el.addClass(this.invalidClass);
13692         } else {
13693             this.inputEl().addClass('is-invalid');
13694         }
13695         
13696         // fixme ... this may be depricated need to test..
13697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13698             
13699             var feedback = this.el.select('.form-control-feedback', true).first();
13700             
13701             if(feedback){
13702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13703                 
13704                 if(this.getValue().length || this.forceFeedback){
13705                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13706                 }
13707                 
13708             }
13709             
13710         }
13711         
13712         this.fireEvent('invalid', this, msg);
13713     }
13714 });
13715
13716  
13717 /*
13718  * - LGPL
13719  *
13720  * trigger field - base class for combo..
13721  * 
13722  */
13723  
13724 /**
13725  * @class Roo.bootstrap.form.TriggerField
13726  * @extends Roo.bootstrap.form.Input
13727  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13728  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13729  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13730  * for which you can provide a custom implementation.  For example:
13731  * <pre><code>
13732 var trigger = new Roo.bootstrap.form.TriggerField();
13733 trigger.onTriggerClick = myTriggerFn;
13734 trigger.applyTo('my-field');
13735 </code></pre>
13736  *
13737  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13738  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13739  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13740  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13741  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13742
13743  * @constructor
13744  * Create a new TriggerField.
13745  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13746  * to the base TextField)
13747  */
13748 Roo.bootstrap.form.TriggerField = function(config){
13749     this.mimicing = false;
13750     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13751 };
13752
13753 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13754     /**
13755      * @cfg {String} triggerClass A CSS class to apply to the trigger
13756      */
13757      /**
13758      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13759      */
13760     hideTrigger:false,
13761
13762     /**
13763      * @cfg {Boolean} removable (true|false) special filter default false
13764      */
13765     removable : false,
13766     
13767     /** @cfg {Boolean} grow @hide */
13768     /** @cfg {Number} growMin @hide */
13769     /** @cfg {Number} growMax @hide */
13770
13771     /**
13772      * @hide 
13773      * @method
13774      */
13775     autoSize: Roo.emptyFn,
13776     // private
13777     monitorTab : true,
13778     // private
13779     deferHeight : true,
13780
13781     
13782     actionMode : 'wrap',
13783     
13784     caret : false,
13785     
13786     
13787     getAutoCreate : function(){
13788        
13789         var align = this.labelAlign || this.parentLabelAlign();
13790         
13791         var id = Roo.id();
13792         
13793         var cfg = {
13794             cls: 'form-group' //input-group
13795         };
13796         
13797         
13798         var input =  {
13799             tag: 'input',
13800             id : id,
13801             type : this.inputType,
13802             cls : 'form-control',
13803             autocomplete: 'new-password',
13804             placeholder : this.placeholder || '' 
13805             
13806         };
13807         if (this.name) {
13808             input.name = this.name;
13809         }
13810         if (this.size) {
13811             input.cls += ' input-' + this.size;
13812         }
13813         
13814         if (this.disabled) {
13815             input.disabled=true;
13816         }
13817         
13818         var inputblock = input;
13819         
13820         if(this.hasFeedback && !this.allowBlank){
13821             
13822             var feedback = {
13823                 tag: 'span',
13824                 cls: 'glyphicon form-control-feedback'
13825             };
13826             
13827             if(this.removable && !this.editable  ){
13828                 inputblock = {
13829                     cls : 'has-feedback',
13830                     cn :  [
13831                         inputblock,
13832                         {
13833                             tag: 'button',
13834                             html : 'x',
13835                             cls : 'roo-combo-removable-btn close'
13836                         },
13837                         feedback
13838                     ] 
13839                 };
13840             } else {
13841                 inputblock = {
13842                     cls : 'has-feedback',
13843                     cn :  [
13844                         inputblock,
13845                         feedback
13846                     ] 
13847                 };
13848             }
13849
13850         } else {
13851             if(this.removable && !this.editable ){
13852                 inputblock = {
13853                     cls : 'roo-removable',
13854                     cn :  [
13855                         inputblock,
13856                         {
13857                             tag: 'button',
13858                             html : 'x',
13859                             cls : 'roo-combo-removable-btn close'
13860                         }
13861                     ] 
13862                 };
13863             }
13864         }
13865         
13866         if (this.before || this.after) {
13867             
13868             inputblock = {
13869                 cls : 'input-group',
13870                 cn :  [] 
13871             };
13872             if (this.before) {
13873                 inputblock.cn.push({
13874                     tag :'span',
13875                     cls : 'input-group-addon input-group-prepend input-group-text',
13876                     html : this.before
13877                 });
13878             }
13879             
13880             inputblock.cn.push(input);
13881             
13882             if(this.hasFeedback && !this.allowBlank){
13883                 inputblock.cls += ' has-feedback';
13884                 inputblock.cn.push(feedback);
13885             }
13886             
13887             if (this.after) {
13888                 inputblock.cn.push({
13889                     tag :'span',
13890                     cls : 'input-group-addon input-group-append input-group-text',
13891                     html : this.after
13892                 });
13893             }
13894             
13895         };
13896         
13897       
13898         
13899         var ibwrap = inputblock;
13900         
13901         if(this.multiple){
13902             ibwrap = {
13903                 tag: 'ul',
13904                 cls: 'roo-select2-choices',
13905                 cn:[
13906                     {
13907                         tag: 'li',
13908                         cls: 'roo-select2-search-field',
13909                         cn: [
13910
13911                             inputblock
13912                         ]
13913                     }
13914                 ]
13915             };
13916                 
13917         }
13918         
13919         var combobox = {
13920             cls: 'roo-select2-container input-group',
13921             cn: [
13922                  {
13923                     tag: 'input',
13924                     type : 'hidden',
13925                     cls: 'form-hidden-field'
13926                 },
13927                 ibwrap
13928             ]
13929         };
13930         
13931         if(!this.multiple && this.showToggleBtn){
13932             
13933             var caret = {
13934                         tag: 'span',
13935                         cls: 'caret'
13936              };
13937             if (this.caret != false) {
13938                 caret = {
13939                      tag: 'i',
13940                      cls: 'fa fa-' + this.caret
13941                 };
13942                 
13943             }
13944             
13945             combobox.cn.push({
13946                 tag :'span',
13947                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13948                 cn : [
13949                     Roo.bootstrap.version == 3 ? caret : '',
13950                     {
13951                         tag: 'span',
13952                         cls: 'combobox-clear',
13953                         cn  : [
13954                             {
13955                                 tag : 'i',
13956                                 cls: 'icon-remove'
13957                             }
13958                         ]
13959                     }
13960                 ]
13961
13962             })
13963         }
13964         
13965         if(this.multiple){
13966             combobox.cls += ' roo-select2-container-multi';
13967         }
13968          var indicator = {
13969             tag : 'i',
13970             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13971             tooltip : 'This field is required'
13972         };
13973         if (Roo.bootstrap.version == 4) {
13974             indicator = {
13975                 tag : 'i',
13976                 style : 'display:none'
13977             };
13978         }
13979         
13980         
13981         if (align ==='left' && this.fieldLabel.length) {
13982             
13983             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13984
13985             cfg.cn = [
13986                 indicator,
13987                 {
13988                     tag: 'label',
13989                     'for' :  id,
13990                     cls : 'control-label',
13991                     html : this.fieldLabel
13992
13993                 },
13994                 {
13995                     cls : "", 
13996                     cn: [
13997                         combobox
13998                     ]
13999                 }
14000
14001             ];
14002             
14003             var labelCfg = cfg.cn[1];
14004             var contentCfg = cfg.cn[2];
14005             
14006             if(this.indicatorpos == 'right'){
14007                 cfg.cn = [
14008                     {
14009                         tag: 'label',
14010                         'for' :  id,
14011                         cls : 'control-label',
14012                         cn : [
14013                             {
14014                                 tag : 'span',
14015                                 html : this.fieldLabel
14016                             },
14017                             indicator
14018                         ]
14019                     },
14020                     {
14021                         cls : "", 
14022                         cn: [
14023                             combobox
14024                         ]
14025                     }
14026
14027                 ];
14028                 
14029                 labelCfg = cfg.cn[0];
14030                 contentCfg = cfg.cn[1];
14031             }
14032             
14033             if(this.labelWidth > 12){
14034                 labelCfg.style = "width: " + this.labelWidth + 'px';
14035             }
14036             
14037             if(this.labelWidth < 13 && this.labelmd == 0){
14038                 this.labelmd = this.labelWidth;
14039             }
14040             
14041             if(this.labellg > 0){
14042                 labelCfg.cls += ' col-lg-' + this.labellg;
14043                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14044             }
14045             
14046             if(this.labelmd > 0){
14047                 labelCfg.cls += ' col-md-' + this.labelmd;
14048                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14049             }
14050             
14051             if(this.labelsm > 0){
14052                 labelCfg.cls += ' col-sm-' + this.labelsm;
14053                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14054             }
14055             
14056             if(this.labelxs > 0){
14057                 labelCfg.cls += ' col-xs-' + this.labelxs;
14058                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14059             }
14060             
14061         } else if ( this.fieldLabel.length) {
14062 //                Roo.log(" label");
14063             cfg.cn = [
14064                 indicator,
14065                {
14066                    tag: 'label',
14067                    //cls : 'input-group-addon',
14068                    html : this.fieldLabel
14069
14070                },
14071
14072                combobox
14073
14074             ];
14075             
14076             if(this.indicatorpos == 'right'){
14077                 
14078                 cfg.cn = [
14079                     {
14080                        tag: 'label',
14081                        cn : [
14082                            {
14083                                tag : 'span',
14084                                html : this.fieldLabel
14085                            },
14086                            indicator
14087                        ]
14088
14089                     },
14090                     combobox
14091
14092                 ];
14093
14094             }
14095
14096         } else {
14097             
14098 //                Roo.log(" no label && no align");
14099                 cfg = combobox
14100                      
14101                 
14102         }
14103         
14104         var settings=this;
14105         ['xs','sm','md','lg'].map(function(size){
14106             if (settings[size]) {
14107                 cfg.cls += ' col-' + size + '-' + settings[size];
14108             }
14109         });
14110         
14111         return cfg;
14112         
14113     },
14114     
14115     
14116     
14117     // private
14118     onResize : function(w, h){
14119 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14120 //        if(typeof w == 'number'){
14121 //            var x = w - this.trigger.getWidth();
14122 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14123 //            this.trigger.setStyle('left', x+'px');
14124 //        }
14125     },
14126
14127     // private
14128     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14129
14130     // private
14131     getResizeEl : function(){
14132         return this.inputEl();
14133     },
14134
14135     // private
14136     getPositionEl : function(){
14137         return this.inputEl();
14138     },
14139
14140     // private
14141     alignErrorIcon : function(){
14142         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14143     },
14144
14145     // private
14146     initEvents : function(){
14147         
14148         this.createList();
14149         
14150         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14151         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14152         if(!this.multiple && this.showToggleBtn){
14153             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14154             if(this.hideTrigger){
14155                 this.trigger.setDisplayed(false);
14156             }
14157             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14158         }
14159         
14160         if(this.multiple){
14161             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14162         }
14163         
14164         if(this.removable && !this.editable && !this.tickable){
14165             var close = this.closeTriggerEl();
14166             
14167             if(close){
14168                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14169                 close.on('click', this.removeBtnClick, this, close);
14170             }
14171         }
14172         
14173         //this.trigger.addClassOnOver('x-form-trigger-over');
14174         //this.trigger.addClassOnClick('x-form-trigger-click');
14175         
14176         //if(!this.width){
14177         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14178         //}
14179     },
14180     
14181     closeTriggerEl : function()
14182     {
14183         var close = this.el.select('.roo-combo-removable-btn', true).first();
14184         return close ? close : false;
14185     },
14186     
14187     removeBtnClick : function(e, h, el)
14188     {
14189         e.preventDefault();
14190         
14191         if(this.fireEvent("remove", this) !== false){
14192             this.reset();
14193             this.fireEvent("afterremove", this)
14194         }
14195     },
14196     
14197     createList : function()
14198     {
14199         this.list = Roo.get(document.body).createChild({
14200             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14201             cls: 'typeahead typeahead-long dropdown-menu shadow',
14202             style: 'display:none'
14203         });
14204         
14205         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14206         
14207     },
14208
14209     // private
14210     initTrigger : function(){
14211        
14212     },
14213
14214     // private
14215     onDestroy : function(){
14216         if(this.trigger){
14217             this.trigger.removeAllListeners();
14218           //  this.trigger.remove();
14219         }
14220         //if(this.wrap){
14221         //    this.wrap.remove();
14222         //}
14223         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14224     },
14225
14226     // private
14227     onFocus : function(){
14228         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14229         /*
14230         if(!this.mimicing){
14231             this.wrap.addClass('x-trigger-wrap-focus');
14232             this.mimicing = true;
14233             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14234             if(this.monitorTab){
14235                 this.el.on("keydown", this.checkTab, this);
14236             }
14237         }
14238         */
14239     },
14240
14241     // private
14242     checkTab : function(e){
14243         if(e.getKey() == e.TAB){
14244             this.triggerBlur();
14245         }
14246     },
14247
14248     // private
14249     onBlur : function(){
14250         // do nothing
14251     },
14252
14253     // private
14254     mimicBlur : function(e, t){
14255         /*
14256         if(!this.wrap.contains(t) && this.validateBlur()){
14257             this.triggerBlur();
14258         }
14259         */
14260     },
14261
14262     // private
14263     triggerBlur : function(){
14264         this.mimicing = false;
14265         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14266         if(this.monitorTab){
14267             this.el.un("keydown", this.checkTab, this);
14268         }
14269         //this.wrap.removeClass('x-trigger-wrap-focus');
14270         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14271     },
14272
14273     // private
14274     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14275     validateBlur : function(e, t){
14276         return true;
14277     },
14278
14279     // private
14280     onDisable : function(){
14281         this.inputEl().dom.disabled = true;
14282         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14283         //if(this.wrap){
14284         //    this.wrap.addClass('x-item-disabled');
14285         //}
14286     },
14287
14288     // private
14289     onEnable : function(){
14290         this.inputEl().dom.disabled = false;
14291         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14292         //if(this.wrap){
14293         //    this.el.removeClass('x-item-disabled');
14294         //}
14295     },
14296
14297     // private
14298     onShow : function(){
14299         var ae = this.getActionEl();
14300         
14301         if(ae){
14302             ae.dom.style.display = '';
14303             ae.dom.style.visibility = 'visible';
14304         }
14305     },
14306
14307     // private
14308     
14309     onHide : function(){
14310         var ae = this.getActionEl();
14311         ae.dom.style.display = 'none';
14312     },
14313
14314     /**
14315      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14316      * by an implementing function.
14317      * @method
14318      * @param {EventObject} e
14319      */
14320     onTriggerClick : Roo.emptyFn
14321 });
14322  
14323 /*
14324 * Licence: LGPL
14325 */
14326
14327 /**
14328  * @class Roo.bootstrap.form.CardUploader
14329  * @extends Roo.bootstrap.Button
14330  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14331  * @cfg {Number} errorTimeout default 3000
14332  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14333  * @cfg {Array}  html The button text.
14334
14335  *
14336  * @constructor
14337  * Create a new CardUploader
14338  * @param {Object} config The config object
14339  */
14340
14341 Roo.bootstrap.form.CardUploader = function(config){
14342     
14343  
14344     
14345     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14346     
14347     
14348     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14349         return r.data.id
14350      });
14351     
14352      this.addEvents({
14353          // raw events
14354         /**
14355          * @event preview
14356          * When a image is clicked on - and needs to display a slideshow or similar..
14357          * @param {Roo.bootstrap.Card} this
14358          * @param {Object} The image information data 
14359          *
14360          */
14361         'preview' : true,
14362          /**
14363          * @event download
14364          * When a the download link is clicked
14365          * @param {Roo.bootstrap.Card} this
14366          * @param {Object} The image information data  contains 
14367          */
14368         'download' : true
14369         
14370     });
14371 };
14372  
14373 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14374     
14375      
14376     errorTimeout : 3000,
14377      
14378     images : false,
14379    
14380     fileCollection : false,
14381     allowBlank : true,
14382     
14383     getAutoCreate : function()
14384     {
14385         
14386         var cfg =  {
14387             cls :'form-group' ,
14388             cn : [
14389                
14390                 {
14391                     tag: 'label',
14392                    //cls : 'input-group-addon',
14393                     html : this.fieldLabel
14394
14395                 },
14396
14397                 {
14398                     tag: 'input',
14399                     type : 'hidden',
14400                     name : this.name,
14401                     value : this.value,
14402                     cls : 'd-none  form-control'
14403                 },
14404                 
14405                 {
14406                     tag: 'input',
14407                     multiple : 'multiple',
14408                     type : 'file',
14409                     cls : 'd-none  roo-card-upload-selector'
14410                 },
14411                 
14412                 {
14413                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14414                 },
14415                 {
14416                     cls : 'card-columns roo-card-uploader-container'
14417                 }
14418
14419             ]
14420         };
14421            
14422          
14423         return cfg;
14424     },
14425     
14426     getChildContainer : function() /// what children are added to.
14427     {
14428         return this.containerEl;
14429     },
14430    
14431     getButtonContainer : function() /// what children are added to.
14432     {
14433         return this.el.select(".roo-card-uploader-button-container").first();
14434     },
14435    
14436     initEvents : function()
14437     {
14438         
14439         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14440         
14441         var t = this;
14442         this.addxtype({
14443             xns: Roo.bootstrap,
14444
14445             xtype : 'Button',
14446             container_method : 'getButtonContainer' ,            
14447             html :  this.html, // fix changable?
14448             cls : 'w-100 ',
14449             listeners : {
14450                 'click' : function(btn, e) {
14451                     t.onClick(e);
14452                 }
14453             }
14454         });
14455         
14456         
14457         
14458         
14459         this.urlAPI = (window.createObjectURL && window) || 
14460                                 (window.URL && URL.revokeObjectURL && URL) || 
14461                                 (window.webkitURL && webkitURL);
14462                         
14463          
14464          
14465          
14466         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14467         
14468         this.selectorEl.on('change', this.onFileSelected, this);
14469         if (this.images) {
14470             var t = this;
14471             this.images.forEach(function(img) {
14472                 t.addCard(img)
14473             });
14474             this.images = false;
14475         }
14476         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14477          
14478        
14479     },
14480     
14481    
14482     onClick : function(e)
14483     {
14484         e.preventDefault();
14485          
14486         this.selectorEl.dom.click();
14487          
14488     },
14489     
14490     onFileSelected : function(e)
14491     {
14492         e.preventDefault();
14493         
14494         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14495             return;
14496         }
14497         
14498         Roo.each(this.selectorEl.dom.files, function(file){    
14499             this.addFile(file);
14500         }, this);
14501          
14502     },
14503     
14504       
14505     
14506       
14507     
14508     addFile : function(file)
14509     {
14510            
14511         if(typeof(file) === 'string'){
14512             throw "Add file by name?"; // should not happen
14513             return;
14514         }
14515         
14516         if(!file || !this.urlAPI){
14517             return;
14518         }
14519         
14520         // file;
14521         // file.type;
14522         
14523         var _this = this;
14524         
14525         
14526         var url = _this.urlAPI.createObjectURL( file);
14527            
14528         this.addCard({
14529             id : Roo.bootstrap.form.CardUploader.ID--,
14530             is_uploaded : false,
14531             src : url,
14532             srcfile : file,
14533             title : file.name,
14534             mimetype : file.type,
14535             preview : false,
14536             is_deleted : 0
14537         });
14538         
14539     },
14540     
14541     /**
14542      * addCard - add an Attachment to the uploader
14543      * @param data - the data about the image to upload
14544      *
14545      * {
14546           id : 123
14547           title : "Title of file",
14548           is_uploaded : false,
14549           src : "http://.....",
14550           srcfile : { the File upload object },
14551           mimetype : file.type,
14552           preview : false,
14553           is_deleted : 0
14554           .. any other data...
14555         }
14556      *
14557      * 
14558     */
14559     
14560     addCard : function (data)
14561     {
14562         // hidden input element?
14563         // if the file is not an image...
14564         //then we need to use something other that and header_image
14565         var t = this;
14566         //   remove.....
14567         var footer = [
14568             {
14569                 xns : Roo.bootstrap,
14570                 xtype : 'CardFooter',
14571                  items: [
14572                     {
14573                         xns : Roo.bootstrap,
14574                         xtype : 'Element',
14575                         cls : 'd-flex',
14576                         items : [
14577                             
14578                             {
14579                                 xns : Roo.bootstrap,
14580                                 xtype : 'Button',
14581                                 html : String.format("<small>{0}</small>", data.title),
14582                                 cls : 'col-10 text-left',
14583                                 size: 'sm',
14584                                 weight: 'link',
14585                                 fa : 'download',
14586                                 listeners : {
14587                                     click : function() {
14588                                      
14589                                         t.fireEvent( "download", t, data );
14590                                     }
14591                                 }
14592                             },
14593                           
14594                             {
14595                                 xns : Roo.bootstrap,
14596                                 xtype : 'Button',
14597                                 style: 'max-height: 28px; ',
14598                                 size : 'sm',
14599                                 weight: 'danger',
14600                                 cls : 'col-2',
14601                                 fa : 'times',
14602                                 listeners : {
14603                                     click : function() {
14604                                         t.removeCard(data.id)
14605                                     }
14606                                 }
14607                             }
14608                         ]
14609                     }
14610                     
14611                 ] 
14612             }
14613             
14614         ];
14615         
14616         var cn = this.addxtype(
14617             {
14618                  
14619                 xns : Roo.bootstrap,
14620                 xtype : 'Card',
14621                 closeable : true,
14622                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14623                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14624                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14625                 data : data,
14626                 html : false,
14627                  
14628                 items : footer,
14629                 initEvents : function() {
14630                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14631                     var card = this;
14632                     this.imgEl = this.el.select('.card-img-top').first();
14633                     if (this.imgEl) {
14634                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14635                         this.imgEl.set({ 'pointer' : 'cursor' });
14636                                   
14637                     }
14638                     this.getCardFooter().addClass('p-1');
14639                     
14640                   
14641                 }
14642                 
14643             }
14644         );
14645         // dont' really need ot update items.
14646         // this.items.push(cn);
14647         this.fileCollection.add(cn);
14648         
14649         if (!data.srcfile) {
14650             this.updateInput();
14651             return;
14652         }
14653             
14654         var _t = this;
14655         var reader = new FileReader();
14656         reader.addEventListener("load", function() {  
14657             data.srcdata =  reader.result;
14658             _t.updateInput();
14659         });
14660         reader.readAsDataURL(data.srcfile);
14661         
14662         
14663         
14664     },
14665     removeCard : function(id)
14666     {
14667         
14668         var card  = this.fileCollection.get(id);
14669         card.data.is_deleted = 1;
14670         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14671         //this.fileCollection.remove(card);
14672         //this.items = this.items.filter(function(e) { return e != card });
14673         // dont' really need ot update items.
14674         card.el.dom.parentNode.removeChild(card.el.dom);
14675         this.updateInput();
14676
14677         
14678     },
14679     reset: function()
14680     {
14681         this.fileCollection.each(function(card) {
14682             if (card.el.dom && card.el.dom.parentNode) {
14683                 card.el.dom.parentNode.removeChild(card.el.dom);
14684             }
14685         });
14686         this.fileCollection.clear();
14687         this.updateInput();
14688     },
14689     
14690     updateInput : function()
14691     {
14692          var data = [];
14693         this.fileCollection.each(function(e) {
14694             data.push(e.data);
14695             
14696         });
14697         this.inputEl().dom.value = JSON.stringify(data);
14698         
14699         
14700         
14701     }
14702     
14703     
14704 });
14705
14706
14707 Roo.bootstrap.form.CardUploader.ID = -1;/*
14708  * Based on:
14709  * Ext JS Library 1.1.1
14710  * Copyright(c) 2006-2007, Ext JS, LLC.
14711  *
14712  * Originally Released Under LGPL - original licence link has changed is not relivant.
14713  *
14714  * Fork - LGPL
14715  * <script type="text/javascript">
14716  */
14717
14718
14719 /**
14720  * @class Roo.data.SortTypes
14721  * @static
14722  * Defines the default sorting (casting?) comparison functions used when sorting data.
14723  */
14724 Roo.data.SortTypes = {
14725     /**
14726      * Default sort that does nothing
14727      * @param {Mixed} s The value being converted
14728      * @return {Mixed} The comparison value
14729      */
14730     none : function(s){
14731         return s;
14732     },
14733     
14734     /**
14735      * The regular expression used to strip tags
14736      * @type {RegExp}
14737      * @property
14738      */
14739     stripTagsRE : /<\/?[^>]+>/gi,
14740     
14741     /**
14742      * Strips all HTML tags to sort on text only
14743      * @param {Mixed} s The value being converted
14744      * @return {String} The comparison value
14745      */
14746     asText : function(s){
14747         return String(s).replace(this.stripTagsRE, "");
14748     },
14749     
14750     /**
14751      * Strips all HTML tags to sort on text only - Case insensitive
14752      * @param {Mixed} s The value being converted
14753      * @return {String} The comparison value
14754      */
14755     asUCText : function(s){
14756         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14757     },
14758     
14759     /**
14760      * Case insensitive string
14761      * @param {Mixed} s The value being converted
14762      * @return {String} The comparison value
14763      */
14764     asUCString : function(s) {
14765         return String(s).toUpperCase();
14766     },
14767     
14768     /**
14769      * Date sorting
14770      * @param {Mixed} s The value being converted
14771      * @return {Number} The comparison value
14772      */
14773     asDate : function(s) {
14774         if(!s){
14775             return 0;
14776         }
14777         if(s instanceof Date){
14778             return s.getTime();
14779         }
14780         return Date.parse(String(s));
14781     },
14782     
14783     /**
14784      * Float sorting
14785      * @param {Mixed} s The value being converted
14786      * @return {Float} The comparison value
14787      */
14788     asFloat : function(s) {
14789         var val = parseFloat(String(s).replace(/,/g, ""));
14790         if(isNaN(val)) {
14791             val = 0;
14792         }
14793         return val;
14794     },
14795     
14796     /**
14797      * Integer sorting
14798      * @param {Mixed} s The value being converted
14799      * @return {Number} The comparison value
14800      */
14801     asInt : function(s) {
14802         var val = parseInt(String(s).replace(/,/g, ""));
14803         if(isNaN(val)) {
14804             val = 0;
14805         }
14806         return val;
14807     }
14808 };/*
14809  * Based on:
14810  * Ext JS Library 1.1.1
14811  * Copyright(c) 2006-2007, Ext JS, LLC.
14812  *
14813  * Originally Released Under LGPL - original licence link has changed is not relivant.
14814  *
14815  * Fork - LGPL
14816  * <script type="text/javascript">
14817  */
14818
14819 /**
14820 * @class Roo.data.Record
14821  * Instances of this class encapsulate both record <em>definition</em> information, and record
14822  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14823  * to access Records cached in an {@link Roo.data.Store} object.<br>
14824  * <p>
14825  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14826  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14827  * objects.<br>
14828  * <p>
14829  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14830  * @constructor
14831  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14832  * {@link #create}. The parameters are the same.
14833  * @param {Array} data An associative Array of data values keyed by the field name.
14834  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14835  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14836  * not specified an integer id is generated.
14837  */
14838 Roo.data.Record = function(data, id){
14839     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14840     this.data = data;
14841 };
14842
14843 /**
14844  * Generate a constructor for a specific record layout.
14845  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14846  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14847  * Each field definition object may contain the following properties: <ul>
14848  * <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,
14849  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14850  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14851  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14852  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14853  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14854  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14855  * this may be omitted.</p></li>
14856  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14857  * <ul><li>auto (Default, implies no conversion)</li>
14858  * <li>string</li>
14859  * <li>int</li>
14860  * <li>float</li>
14861  * <li>boolean</li>
14862  * <li>date</li></ul></p></li>
14863  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14864  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14865  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14866  * by the Reader into an object that will be stored in the Record. It is passed the
14867  * following parameters:<ul>
14868  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14869  * </ul></p></li>
14870  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14871  * </ul>
14872  * <br>usage:<br><pre><code>
14873 var TopicRecord = Roo.data.Record.create(
14874     {name: 'title', mapping: 'topic_title'},
14875     {name: 'author', mapping: 'username'},
14876     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14877     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14878     {name: 'lastPoster', mapping: 'user2'},
14879     {name: 'excerpt', mapping: 'post_text'}
14880 );
14881
14882 var myNewRecord = new TopicRecord({
14883     title: 'Do my job please',
14884     author: 'noobie',
14885     totalPosts: 1,
14886     lastPost: new Date(),
14887     lastPoster: 'Animal',
14888     excerpt: 'No way dude!'
14889 });
14890 myStore.add(myNewRecord);
14891 </code></pre>
14892  * @method create
14893  * @static
14894  */
14895 Roo.data.Record.create = function(o){
14896     var f = function(){
14897         f.superclass.constructor.apply(this, arguments);
14898     };
14899     Roo.extend(f, Roo.data.Record);
14900     var p = f.prototype;
14901     p.fields = new Roo.util.MixedCollection(false, function(field){
14902         return field.name;
14903     });
14904     for(var i = 0, len = o.length; i < len; i++){
14905         p.fields.add(new Roo.data.Field(o[i]));
14906     }
14907     f.getField = function(name){
14908         return p.fields.get(name);  
14909     };
14910     return f;
14911 };
14912
14913 Roo.data.Record.AUTO_ID = 1000;
14914 Roo.data.Record.EDIT = 'edit';
14915 Roo.data.Record.REJECT = 'reject';
14916 Roo.data.Record.COMMIT = 'commit';
14917
14918 Roo.data.Record.prototype = {
14919     /**
14920      * Readonly flag - true if this record has been modified.
14921      * @type Boolean
14922      */
14923     dirty : false,
14924     editing : false,
14925     error: null,
14926     modified: null,
14927
14928     // private
14929     join : function(store){
14930         this.store = store;
14931     },
14932
14933     /**
14934      * Set the named field to the specified value.
14935      * @param {String} name The name of the field to set.
14936      * @param {Object} value The value to set the field to.
14937      */
14938     set : function(name, value){
14939         if(this.data[name] == value){
14940             return;
14941         }
14942         this.dirty = true;
14943         if(!this.modified){
14944             this.modified = {};
14945         }
14946         if(typeof this.modified[name] == 'undefined'){
14947             this.modified[name] = this.data[name];
14948         }
14949         this.data[name] = value;
14950         if(!this.editing && this.store){
14951             this.store.afterEdit(this);
14952         }       
14953     },
14954
14955     /**
14956      * Get the value of the named field.
14957      * @param {String} name The name of the field to get the value of.
14958      * @return {Object} The value of the field.
14959      */
14960     get : function(name){
14961         return this.data[name]; 
14962     },
14963
14964     // private
14965     beginEdit : function(){
14966         this.editing = true;
14967         this.modified = {}; 
14968     },
14969
14970     // private
14971     cancelEdit : function(){
14972         this.editing = false;
14973         delete this.modified;
14974     },
14975
14976     // private
14977     endEdit : function(){
14978         this.editing = false;
14979         if(this.dirty && this.store){
14980             this.store.afterEdit(this);
14981         }
14982     },
14983
14984     /**
14985      * Usually called by the {@link Roo.data.Store} which owns the Record.
14986      * Rejects all changes made to the Record since either creation, or the last commit operation.
14987      * Modified fields are reverted to their original values.
14988      * <p>
14989      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14990      * of reject operations.
14991      */
14992     reject : function(){
14993         var m = this.modified;
14994         for(var n in m){
14995             if(typeof m[n] != "function"){
14996                 this.data[n] = m[n];
14997             }
14998         }
14999         this.dirty = false;
15000         delete this.modified;
15001         this.editing = false;
15002         if(this.store){
15003             this.store.afterReject(this);
15004         }
15005     },
15006
15007     /**
15008      * Usually called by the {@link Roo.data.Store} which owns the Record.
15009      * Commits all changes made to the Record since either creation, or the last commit operation.
15010      * <p>
15011      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15012      * of commit operations.
15013      */
15014     commit : function(){
15015         this.dirty = false;
15016         delete this.modified;
15017         this.editing = false;
15018         if(this.store){
15019             this.store.afterCommit(this);
15020         }
15021     },
15022
15023     // private
15024     hasError : function(){
15025         return this.error != null;
15026     },
15027
15028     // private
15029     clearError : function(){
15030         this.error = null;
15031     },
15032
15033     /**
15034      * Creates a copy of this record.
15035      * @param {String} id (optional) A new record id if you don't want to use this record's id
15036      * @return {Record}
15037      */
15038     copy : function(newId) {
15039         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15040     }
15041 };/*
15042  * Based on:
15043  * Ext JS Library 1.1.1
15044  * Copyright(c) 2006-2007, Ext JS, LLC.
15045  *
15046  * Originally Released Under LGPL - original licence link has changed is not relivant.
15047  *
15048  * Fork - LGPL
15049  * <script type="text/javascript">
15050  */
15051
15052
15053
15054 /**
15055  * @class Roo.data.Store
15056  * @extends Roo.util.Observable
15057  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15058  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15059  * <p>
15060  * 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
15061  * has no knowledge of the format of the data returned by the Proxy.<br>
15062  * <p>
15063  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15064  * instances from the data object. These records are cached and made available through accessor functions.
15065  * @constructor
15066  * Creates a new Store.
15067  * @param {Object} config A config object containing the objects needed for the Store to access data,
15068  * and read the data into Records.
15069  */
15070 Roo.data.Store = function(config){
15071     this.data = new Roo.util.MixedCollection(false);
15072     this.data.getKey = function(o){
15073         return o.id;
15074     };
15075     this.baseParams = {};
15076     // private
15077     this.paramNames = {
15078         "start" : "start",
15079         "limit" : "limit",
15080         "sort" : "sort",
15081         "dir" : "dir",
15082         "multisort" : "_multisort"
15083     };
15084
15085     if(config && config.data){
15086         this.inlineData = config.data;
15087         delete config.data;
15088     }
15089
15090     Roo.apply(this, config);
15091     
15092     if(this.reader){ // reader passed
15093         this.reader = Roo.factory(this.reader, Roo.data);
15094         this.reader.xmodule = this.xmodule || false;
15095         if(!this.recordType){
15096             this.recordType = this.reader.recordType;
15097         }
15098         if(this.reader.onMetaChange){
15099             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15100         }
15101     }
15102
15103     if(this.recordType){
15104         this.fields = this.recordType.prototype.fields;
15105     }
15106     this.modified = [];
15107
15108     this.addEvents({
15109         /**
15110          * @event datachanged
15111          * Fires when the data cache has changed, and a widget which is using this Store
15112          * as a Record cache should refresh its view.
15113          * @param {Store} this
15114          */
15115         datachanged : true,
15116         /**
15117          * @event metachange
15118          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15119          * @param {Store} this
15120          * @param {Object} meta The JSON metadata
15121          */
15122         metachange : true,
15123         /**
15124          * @event add
15125          * Fires when Records have been added to the Store
15126          * @param {Store} this
15127          * @param {Roo.data.Record[]} records The array of Records added
15128          * @param {Number} index The index at which the record(s) were added
15129          */
15130         add : true,
15131         /**
15132          * @event remove
15133          * Fires when a Record has been removed from the Store
15134          * @param {Store} this
15135          * @param {Roo.data.Record} record The Record that was removed
15136          * @param {Number} index The index at which the record was removed
15137          */
15138         remove : true,
15139         /**
15140          * @event update
15141          * Fires when a Record has been updated
15142          * @param {Store} this
15143          * @param {Roo.data.Record} record The Record that was updated
15144          * @param {String} operation The update operation being performed.  Value may be one of:
15145          * <pre><code>
15146  Roo.data.Record.EDIT
15147  Roo.data.Record.REJECT
15148  Roo.data.Record.COMMIT
15149          * </code></pre>
15150          */
15151         update : true,
15152         /**
15153          * @event clear
15154          * Fires when the data cache has been cleared.
15155          * @param {Store} this
15156          */
15157         clear : true,
15158         /**
15159          * @event beforeload
15160          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15161          * the load action will be canceled.
15162          * @param {Store} this
15163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15164          */
15165         beforeload : true,
15166         /**
15167          * @event beforeloadadd
15168          * Fires after a new set of Records has been loaded.
15169          * @param {Store} this
15170          * @param {Roo.data.Record[]} records The Records that were loaded
15171          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15172          */
15173         beforeloadadd : true,
15174         /**
15175          * @event load
15176          * Fires after a new set of Records has been loaded, before they are added to the store.
15177          * @param {Store} this
15178          * @param {Roo.data.Record[]} records The Records that were loaded
15179          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15180          * @params {Object} return from reader
15181          */
15182         load : true,
15183         /**
15184          * @event loadexception
15185          * Fires if an exception occurs in the Proxy during loading.
15186          * Called with the signature of the Proxy's "loadexception" event.
15187          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15188          * 
15189          * @param {Proxy} 
15190          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15191          * @param {Object} load options 
15192          * @param {Object} jsonData from your request (normally this contains the Exception)
15193          */
15194         loadexception : true
15195     });
15196     
15197     if(this.proxy){
15198         this.proxy = Roo.factory(this.proxy, Roo.data);
15199         this.proxy.xmodule = this.xmodule || false;
15200         this.relayEvents(this.proxy,  ["loadexception"]);
15201     }
15202     this.sortToggle = {};
15203     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15204
15205     Roo.data.Store.superclass.constructor.call(this);
15206
15207     if(this.inlineData){
15208         this.loadData(this.inlineData);
15209         delete this.inlineData;
15210     }
15211 };
15212
15213 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15214      /**
15215     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15216     * without a remote query - used by combo/forms at present.
15217     */
15218     
15219     /**
15220     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15221     */
15222     /**
15223     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15224     */
15225     /**
15226     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15227     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15228     */
15229     /**
15230     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15231     * on any HTTP request
15232     */
15233     /**
15234     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15235     */
15236     /**
15237     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15238     */
15239     multiSort: false,
15240     /**
15241     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15242     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15243     */
15244     remoteSort : false,
15245
15246     /**
15247     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15248      * loaded or when a record is removed. (defaults to false).
15249     */
15250     pruneModifiedRecords : false,
15251
15252     // private
15253     lastOptions : null,
15254
15255     /**
15256      * Add Records to the Store and fires the add event.
15257      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15258      */
15259     add : function(records){
15260         records = [].concat(records);
15261         for(var i = 0, len = records.length; i < len; i++){
15262             records[i].join(this);
15263         }
15264         var index = this.data.length;
15265         this.data.addAll(records);
15266         this.fireEvent("add", this, records, index);
15267     },
15268
15269     /**
15270      * Remove a Record from the Store and fires the remove event.
15271      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15272      */
15273     remove : function(record){
15274         var index = this.data.indexOf(record);
15275         this.data.removeAt(index);
15276  
15277         if(this.pruneModifiedRecords){
15278             this.modified.remove(record);
15279         }
15280         this.fireEvent("remove", this, record, index);
15281     },
15282
15283     /**
15284      * Remove all Records from the Store and fires the clear event.
15285      */
15286     removeAll : function(){
15287         this.data.clear();
15288         if(this.pruneModifiedRecords){
15289             this.modified = [];
15290         }
15291         this.fireEvent("clear", this);
15292     },
15293
15294     /**
15295      * Inserts Records to the Store at the given index and fires the add event.
15296      * @param {Number} index The start index at which to insert the passed Records.
15297      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15298      */
15299     insert : function(index, records){
15300         records = [].concat(records);
15301         for(var i = 0, len = records.length; i < len; i++){
15302             this.data.insert(index, records[i]);
15303             records[i].join(this);
15304         }
15305         this.fireEvent("add", this, records, index);
15306     },
15307
15308     /**
15309      * Get the index within the cache of the passed Record.
15310      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15311      * @return {Number} The index of the passed Record. Returns -1 if not found.
15312      */
15313     indexOf : function(record){
15314         return this.data.indexOf(record);
15315     },
15316
15317     /**
15318      * Get the index within the cache of the Record with the passed id.
15319      * @param {String} id The id of the Record to find.
15320      * @return {Number} The index of the Record. Returns -1 if not found.
15321      */
15322     indexOfId : function(id){
15323         return this.data.indexOfKey(id);
15324     },
15325
15326     /**
15327      * Get the Record with the specified id.
15328      * @param {String} id The id of the Record to find.
15329      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15330      */
15331     getById : function(id){
15332         return this.data.key(id);
15333     },
15334
15335     /**
15336      * Get the Record at the specified index.
15337      * @param {Number} index The index of the Record to find.
15338      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15339      */
15340     getAt : function(index){
15341         return this.data.itemAt(index);
15342     },
15343
15344     /**
15345      * Returns a range of Records between specified indices.
15346      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15347      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15348      * @return {Roo.data.Record[]} An array of Records
15349      */
15350     getRange : function(start, end){
15351         return this.data.getRange(start, end);
15352     },
15353
15354     // private
15355     storeOptions : function(o){
15356         o = Roo.apply({}, o);
15357         delete o.callback;
15358         delete o.scope;
15359         this.lastOptions = o;
15360     },
15361
15362     /**
15363      * Loads the Record cache from the configured Proxy using the configured Reader.
15364      * <p>
15365      * If using remote paging, then the first load call must specify the <em>start</em>
15366      * and <em>limit</em> properties in the options.params property to establish the initial
15367      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15368      * <p>
15369      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15370      * and this call will return before the new data has been loaded. Perform any post-processing
15371      * in a callback function, or in a "load" event handler.</strong>
15372      * <p>
15373      * @param {Object} options An object containing properties which control loading options:<ul>
15374      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15375      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15376      * <pre>
15377                 {
15378                     data : data,  // array of key=>value data like JsonReader
15379                     total : data.length,
15380                     success : true
15381                     
15382                 }
15383         </pre>
15384             }.</li>
15385      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15386      * passed the following arguments:<ul>
15387      * <li>r : Roo.data.Record[]</li>
15388      * <li>options: Options object from the load call</li>
15389      * <li>success: Boolean success indicator</li></ul></li>
15390      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15391      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15392      * </ul>
15393      */
15394     load : function(options){
15395         options = options || {};
15396         if(this.fireEvent("beforeload", this, options) !== false){
15397             this.storeOptions(options);
15398             var p = Roo.apply(options.params || {}, this.baseParams);
15399             // if meta was not loaded from remote source.. try requesting it.
15400             if (!this.reader.metaFromRemote) {
15401                 p._requestMeta = 1;
15402             }
15403             if(this.sortInfo && this.remoteSort){
15404                 var pn = this.paramNames;
15405                 p[pn["sort"]] = this.sortInfo.field;
15406                 p[pn["dir"]] = this.sortInfo.direction;
15407             }
15408             if (this.multiSort) {
15409                 var pn = this.paramNames;
15410                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15411             }
15412             
15413             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15414         }
15415     },
15416
15417     /**
15418      * Reloads the Record cache from the configured Proxy using the configured Reader and
15419      * the options from the last load operation performed.
15420      * @param {Object} options (optional) An object containing properties which may override the options
15421      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15422      * the most recently used options are reused).
15423      */
15424     reload : function(options){
15425         this.load(Roo.applyIf(options||{}, this.lastOptions));
15426     },
15427
15428     // private
15429     // Called as a callback by the Reader during a load operation.
15430     loadRecords : function(o, options, success){
15431          
15432         if(!o){
15433             if(success !== false){
15434                 this.fireEvent("load", this, [], options, o);
15435             }
15436             if(options.callback){
15437                 options.callback.call(options.scope || this, [], options, false);
15438             }
15439             return;
15440         }
15441         // if data returned failure - throw an exception.
15442         if (o.success === false) {
15443             // show a message if no listener is registered.
15444             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15445                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15446             }
15447             // loadmask wil be hooked into this..
15448             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15449             return;
15450         }
15451         var r = o.records, t = o.totalRecords || r.length;
15452         
15453         this.fireEvent("beforeloadadd", this, r, options, o);
15454         
15455         if(!options || options.add !== true){
15456             if(this.pruneModifiedRecords){
15457                 this.modified = [];
15458             }
15459             for(var i = 0, len = r.length; i < len; i++){
15460                 r[i].join(this);
15461             }
15462             if(this.snapshot){
15463                 this.data = this.snapshot;
15464                 delete this.snapshot;
15465             }
15466             this.data.clear();
15467             this.data.addAll(r);
15468             this.totalLength = t;
15469             this.applySort();
15470             this.fireEvent("datachanged", this);
15471         }else{
15472             this.totalLength = Math.max(t, this.data.length+r.length);
15473             this.add(r);
15474         }
15475         
15476         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15477                 
15478             var e = new Roo.data.Record({});
15479
15480             e.set(this.parent.displayField, this.parent.emptyTitle);
15481             e.set(this.parent.valueField, '');
15482
15483             this.insert(0, e);
15484         }
15485             
15486         this.fireEvent("load", this, r, options, o);
15487         if(options.callback){
15488             options.callback.call(options.scope || this, r, options, true);
15489         }
15490     },
15491
15492
15493     /**
15494      * Loads data from a passed data block. A Reader which understands the format of the data
15495      * must have been configured in the constructor.
15496      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15497      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15498      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15499      */
15500     loadData : function(o, append){
15501         var r = this.reader.readRecords(o);
15502         this.loadRecords(r, {add: append}, true);
15503     },
15504     
15505      /**
15506      * using 'cn' the nested child reader read the child array into it's child stores.
15507      * @param {Object} rec The record with a 'children array
15508      */
15509     loadDataFromChildren : function(rec)
15510     {
15511         this.loadData(this.reader.toLoadData(rec));
15512     },
15513     
15514
15515     /**
15516      * Gets the number of cached records.
15517      * <p>
15518      * <em>If using paging, this may not be the total size of the dataset. If the data object
15519      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15520      * the data set size</em>
15521      */
15522     getCount : function(){
15523         return this.data.length || 0;
15524     },
15525
15526     /**
15527      * Gets the total number of records in the dataset as returned by the server.
15528      * <p>
15529      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15530      * the dataset size</em>
15531      */
15532     getTotalCount : function(){
15533         return this.totalLength || 0;
15534     },
15535
15536     /**
15537      * Returns the sort state of the Store as an object with two properties:
15538      * <pre><code>
15539  field {String} The name of the field by which the Records are sorted
15540  direction {String} The sort order, "ASC" or "DESC"
15541      * </code></pre>
15542      */
15543     getSortState : function(){
15544         return this.sortInfo;
15545     },
15546
15547     // private
15548     applySort : function(){
15549         if(this.sortInfo && !this.remoteSort){
15550             var s = this.sortInfo, f = s.field;
15551             var st = this.fields.get(f).sortType;
15552             var fn = function(r1, r2){
15553                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15554                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15555             };
15556             this.data.sort(s.direction, fn);
15557             if(this.snapshot && this.snapshot != this.data){
15558                 this.snapshot.sort(s.direction, fn);
15559             }
15560         }
15561     },
15562
15563     /**
15564      * Sets the default sort column and order to be used by the next load operation.
15565      * @param {String} fieldName The name of the field to sort by.
15566      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15567      */
15568     setDefaultSort : function(field, dir){
15569         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15570     },
15571
15572     /**
15573      * Sort the Records.
15574      * If remote sorting is used, the sort is performed on the server, and the cache is
15575      * reloaded. If local sorting is used, the cache is sorted internally.
15576      * @param {String} fieldName The name of the field to sort by.
15577      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15578      */
15579     sort : function(fieldName, dir){
15580         var f = this.fields.get(fieldName);
15581         if(!dir){
15582             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15583             
15584             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15585                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15586             }else{
15587                 dir = f.sortDir;
15588             }
15589         }
15590         this.sortToggle[f.name] = dir;
15591         this.sortInfo = {field: f.name, direction: dir};
15592         if(!this.remoteSort){
15593             this.applySort();
15594             this.fireEvent("datachanged", this);
15595         }else{
15596             this.load(this.lastOptions);
15597         }
15598     },
15599
15600     /**
15601      * Calls the specified function for each of the Records in the cache.
15602      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15603      * Returning <em>false</em> aborts and exits the iteration.
15604      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15605      */
15606     each : function(fn, scope){
15607         this.data.each(fn, scope);
15608     },
15609
15610     /**
15611      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15612      * (e.g., during paging).
15613      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15614      */
15615     getModifiedRecords : function(){
15616         return this.modified;
15617     },
15618
15619     // private
15620     createFilterFn : function(property, value, anyMatch){
15621         if(!value.exec){ // not a regex
15622             value = String(value);
15623             if(value.length == 0){
15624                 return false;
15625             }
15626             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15627         }
15628         return function(r){
15629             return value.test(r.data[property]);
15630         };
15631     },
15632
15633     /**
15634      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15635      * @param {String} property A field on your records
15636      * @param {Number} start The record index to start at (defaults to 0)
15637      * @param {Number} end The last record index to include (defaults to length - 1)
15638      * @return {Number} The sum
15639      */
15640     sum : function(property, start, end){
15641         var rs = this.data.items, v = 0;
15642         start = start || 0;
15643         end = (end || end === 0) ? end : rs.length-1;
15644
15645         for(var i = start; i <= end; i++){
15646             v += (rs[i].data[property] || 0);
15647         }
15648         return v;
15649     },
15650
15651     /**
15652      * Filter the records by a specified property.
15653      * @param {String} field A field on your records
15654      * @param {String/RegExp} value Either a string that the field
15655      * should start with or a RegExp to test against the field
15656      * @param {Boolean} anyMatch True to match any part not just the beginning
15657      */
15658     filter : function(property, value, anyMatch){
15659         var fn = this.createFilterFn(property, value, anyMatch);
15660         return fn ? this.filterBy(fn) : this.clearFilter();
15661     },
15662
15663     /**
15664      * Filter by a function. The specified function will be called with each
15665      * record in this data source. If the function returns true the record is included,
15666      * otherwise it is filtered.
15667      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15668      * @param {Object} scope (optional) The scope of the function (defaults to this)
15669      */
15670     filterBy : function(fn, scope){
15671         this.snapshot = this.snapshot || this.data;
15672         this.data = this.queryBy(fn, scope||this);
15673         this.fireEvent("datachanged", this);
15674     },
15675
15676     /**
15677      * Query the records by a specified property.
15678      * @param {String} field A field on your records
15679      * @param {String/RegExp} value Either a string that the field
15680      * should start with or a RegExp to test against the field
15681      * @param {Boolean} anyMatch True to match any part not just the beginning
15682      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15683      */
15684     query : function(property, value, anyMatch){
15685         var fn = this.createFilterFn(property, value, anyMatch);
15686         return fn ? this.queryBy(fn) : this.data.clone();
15687     },
15688
15689     /**
15690      * Query by a function. The specified function will be called with each
15691      * record in this data source. If the function returns true the record is included
15692      * in the results.
15693      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15694      * @param {Object} scope (optional) The scope of the function (defaults to this)
15695       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15696      **/
15697     queryBy : function(fn, scope){
15698         var data = this.snapshot || this.data;
15699         return data.filterBy(fn, scope||this);
15700     },
15701
15702     /**
15703      * Collects unique values for a particular dataIndex from this store.
15704      * @param {String} dataIndex The property to collect
15705      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15706      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15707      * @return {Array} An array of the unique values
15708      **/
15709     collect : function(dataIndex, allowNull, bypassFilter){
15710         var d = (bypassFilter === true && this.snapshot) ?
15711                 this.snapshot.items : this.data.items;
15712         var v, sv, r = [], l = {};
15713         for(var i = 0, len = d.length; i < len; i++){
15714             v = d[i].data[dataIndex];
15715             sv = String(v);
15716             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15717                 l[sv] = true;
15718                 r[r.length] = v;
15719             }
15720         }
15721         return r;
15722     },
15723
15724     /**
15725      * Revert to a view of the Record cache with no filtering applied.
15726      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15727      */
15728     clearFilter : function(suppressEvent){
15729         if(this.snapshot && this.snapshot != this.data){
15730             this.data = this.snapshot;
15731             delete this.snapshot;
15732             if(suppressEvent !== true){
15733                 this.fireEvent("datachanged", this);
15734             }
15735         }
15736     },
15737
15738     // private
15739     afterEdit : function(record){
15740         if(this.modified.indexOf(record) == -1){
15741             this.modified.push(record);
15742         }
15743         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15744     },
15745     
15746     // private
15747     afterReject : function(record){
15748         this.modified.remove(record);
15749         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15750     },
15751
15752     // private
15753     afterCommit : function(record){
15754         this.modified.remove(record);
15755         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15756     },
15757
15758     /**
15759      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15760      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15761      */
15762     commitChanges : function(){
15763         var m = this.modified.slice(0);
15764         this.modified = [];
15765         for(var i = 0, len = m.length; i < len; i++){
15766             m[i].commit();
15767         }
15768     },
15769
15770     /**
15771      * Cancel outstanding changes on all changed records.
15772      */
15773     rejectChanges : function(){
15774         var m = this.modified.slice(0);
15775         this.modified = [];
15776         for(var i = 0, len = m.length; i < len; i++){
15777             m[i].reject();
15778         }
15779     },
15780
15781     onMetaChange : function(meta, rtype, o){
15782         this.recordType = rtype;
15783         this.fields = rtype.prototype.fields;
15784         delete this.snapshot;
15785         this.sortInfo = meta.sortInfo || this.sortInfo;
15786         this.modified = [];
15787         this.fireEvent('metachange', this, this.reader.meta);
15788     },
15789     
15790     moveIndex : function(data, type)
15791     {
15792         var index = this.indexOf(data);
15793         
15794         var newIndex = index + type;
15795         
15796         this.remove(data);
15797         
15798         this.insert(newIndex, data);
15799         
15800     }
15801 });/*
15802  * Based on:
15803  * Ext JS Library 1.1.1
15804  * Copyright(c) 2006-2007, Ext JS, LLC.
15805  *
15806  * Originally Released Under LGPL - original licence link has changed is not relivant.
15807  *
15808  * Fork - LGPL
15809  * <script type="text/javascript">
15810  */
15811
15812 /**
15813  * @class Roo.data.SimpleStore
15814  * @extends Roo.data.Store
15815  * Small helper class to make creating Stores from Array data easier.
15816  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15817  * @cfg {Array} fields An array of field definition objects, or field name strings.
15818  * @cfg {Object} an existing reader (eg. copied from another store)
15819  * @cfg {Array} data The multi-dimensional array of data
15820  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15821  * @cfg {Roo.data.Reader} reader  [not-required] 
15822  * @constructor
15823  * @param {Object} config
15824  */
15825 Roo.data.SimpleStore = function(config)
15826 {
15827     Roo.data.SimpleStore.superclass.constructor.call(this, {
15828         isLocal : true,
15829         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15830                 id: config.id
15831             },
15832             Roo.data.Record.create(config.fields)
15833         ),
15834         proxy : new Roo.data.MemoryProxy(config.data)
15835     });
15836     this.load();
15837 };
15838 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848
15849 /**
15850 /**
15851  * @extends Roo.data.Store
15852  * @class Roo.data.JsonStore
15853  * Small helper class to make creating Stores for JSON data easier. <br/>
15854 <pre><code>
15855 var store = new Roo.data.JsonStore({
15856     url: 'get-images.php',
15857     root: 'images',
15858     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15859 });
15860 </code></pre>
15861  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15862  * JsonReader and HttpProxy (unless inline data is provided).</b>
15863  * @cfg {Array} fields An array of field definition objects, or field name strings.
15864  * @constructor
15865  * @param {Object} config
15866  */
15867 Roo.data.JsonStore = function(c){
15868     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15869         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15870         reader: new Roo.data.JsonReader(c, c.fields)
15871     }));
15872 };
15873 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15874  * Based on:
15875  * Ext JS Library 1.1.1
15876  * Copyright(c) 2006-2007, Ext JS, LLC.
15877  *
15878  * Originally Released Under LGPL - original licence link has changed is not relivant.
15879  *
15880  * Fork - LGPL
15881  * <script type="text/javascript">
15882  */
15883
15884  
15885 Roo.data.Field = function(config){
15886     if(typeof config == "string"){
15887         config = {name: config};
15888     }
15889     Roo.apply(this, config);
15890     
15891     if(!this.type){
15892         this.type = "auto";
15893     }
15894     
15895     var st = Roo.data.SortTypes;
15896     // named sortTypes are supported, here we look them up
15897     if(typeof this.sortType == "string"){
15898         this.sortType = st[this.sortType];
15899     }
15900     
15901     // set default sortType for strings and dates
15902     if(!this.sortType){
15903         switch(this.type){
15904             case "string":
15905                 this.sortType = st.asUCString;
15906                 break;
15907             case "date":
15908                 this.sortType = st.asDate;
15909                 break;
15910             default:
15911                 this.sortType = st.none;
15912         }
15913     }
15914
15915     // define once
15916     var stripRe = /[\$,%]/g;
15917
15918     // prebuilt conversion function for this field, instead of
15919     // switching every time we're reading a value
15920     if(!this.convert){
15921         var cv, dateFormat = this.dateFormat;
15922         switch(this.type){
15923             case "":
15924             case "auto":
15925             case undefined:
15926                 cv = function(v){ return v; };
15927                 break;
15928             case "string":
15929                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15930                 break;
15931             case "int":
15932                 cv = function(v){
15933                     return v !== undefined && v !== null && v !== '' ?
15934                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15935                     };
15936                 break;
15937             case "float":
15938                 cv = function(v){
15939                     return v !== undefined && v !== null && v !== '' ?
15940                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15941                     };
15942                 break;
15943             case "bool":
15944             case "boolean":
15945                 cv = function(v){ return v === true || v === "true" || v == 1; };
15946                 break;
15947             case "date":
15948                 cv = function(v){
15949                     if(!v){
15950                         return '';
15951                     }
15952                     if(v instanceof Date){
15953                         return v;
15954                     }
15955                     if(dateFormat){
15956                         if(dateFormat == "timestamp"){
15957                             return new Date(v*1000);
15958                         }
15959                         return Date.parseDate(v, dateFormat);
15960                     }
15961                     var parsed = Date.parse(v);
15962                     return parsed ? new Date(parsed) : null;
15963                 };
15964              break;
15965             
15966         }
15967         this.convert = cv;
15968     }
15969 };
15970
15971 Roo.data.Field.prototype = {
15972     dateFormat: null,
15973     defaultValue: "",
15974     mapping: null,
15975     sortType : null,
15976     sortDir : "ASC"
15977 };/*
15978  * Based on:
15979  * Ext JS Library 1.1.1
15980  * Copyright(c) 2006-2007, Ext JS, LLC.
15981  *
15982  * Originally Released Under LGPL - original licence link has changed is not relivant.
15983  *
15984  * Fork - LGPL
15985  * <script type="text/javascript">
15986  */
15987  
15988 // Base class for reading structured data from a data source.  This class is intended to be
15989 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15990
15991 /**
15992  * @class Roo.data.DataReader
15993  * @abstract
15994  * Base class for reading structured data from a data source.  This class is intended to be
15995  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15996  */
15997
15998 Roo.data.DataReader = function(meta, recordType){
15999     
16000     this.meta = meta;
16001     
16002     this.recordType = recordType instanceof Array ? 
16003         Roo.data.Record.create(recordType) : recordType;
16004 };
16005
16006 Roo.data.DataReader.prototype = {
16007     
16008     
16009     readerType : 'Data',
16010      /**
16011      * Create an empty record
16012      * @param {Object} data (optional) - overlay some values
16013      * @return {Roo.data.Record} record created.
16014      */
16015     newRow :  function(d) {
16016         var da =  {};
16017         this.recordType.prototype.fields.each(function(c) {
16018             switch( c.type) {
16019                 case 'int' : da[c.name] = 0; break;
16020                 case 'date' : da[c.name] = new Date(); break;
16021                 case 'float' : da[c.name] = 0.0; break;
16022                 case 'boolean' : da[c.name] = false; break;
16023                 default : da[c.name] = ""; break;
16024             }
16025             
16026         });
16027         return new this.recordType(Roo.apply(da, d));
16028     }
16029     
16030     
16031 };/*
16032  * Based on:
16033  * Ext JS Library 1.1.1
16034  * Copyright(c) 2006-2007, Ext JS, LLC.
16035  *
16036  * Originally Released Under LGPL - original licence link has changed is not relivant.
16037  *
16038  * Fork - LGPL
16039  * <script type="text/javascript">
16040  */
16041
16042 /**
16043  * @class Roo.data.DataProxy
16044  * @extends Roo.util.Observable
16045  * @abstract
16046  * This class is an abstract base class for implementations which provide retrieval of
16047  * unformatted data objects.<br>
16048  * <p>
16049  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16050  * (of the appropriate type which knows how to parse the data object) to provide a block of
16051  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16052  * <p>
16053  * Custom implementations must implement the load method as described in
16054  * {@link Roo.data.HttpProxy#load}.
16055  */
16056 Roo.data.DataProxy = function(){
16057     this.addEvents({
16058         /**
16059          * @event beforeload
16060          * Fires before a network request is made to retrieve a data object.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} params The params parameter to the load function.
16063          */
16064         beforeload : true,
16065         /**
16066          * @event load
16067          * Fires before the load method's callback is called.
16068          * @param {Object} This DataProxy object.
16069          * @param {Object} o The data object.
16070          * @param {Object} arg The callback argument object passed to the load function.
16071          */
16072         load : true,
16073         /**
16074          * @event loadexception
16075          * Fires if an Exception occurs during data retrieval.
16076          * @param {Object} This DataProxy object.
16077          * @param {Object} o The data object.
16078          * @param {Object} arg The callback argument object passed to the load function.
16079          * @param {Object} e The Exception.
16080          */
16081         loadexception : true
16082     });
16083     Roo.data.DataProxy.superclass.constructor.call(this);
16084 };
16085
16086 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16087
16088     /**
16089      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16090      */
16091 /*
16092  * Based on:
16093  * Ext JS Library 1.1.1
16094  * Copyright(c) 2006-2007, Ext JS, LLC.
16095  *
16096  * Originally Released Under LGPL - original licence link has changed is not relivant.
16097  *
16098  * Fork - LGPL
16099  * <script type="text/javascript">
16100  */
16101 /**
16102  * @class Roo.data.MemoryProxy
16103  * @extends Roo.data.DataProxy
16104  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16105  * to the Reader when its load method is called.
16106  * @constructor
16107  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16108  */
16109 Roo.data.MemoryProxy = function(config){
16110     var data = config;
16111     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16112         data = config.data;
16113     }
16114     Roo.data.MemoryProxy.superclass.constructor.call(this);
16115     this.data = data;
16116 };
16117
16118 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16119     
16120     /**
16121      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16122      */
16123     /**
16124      * Load data from the requested source (in this case an in-memory
16125      * data object passed to the constructor), read the data object into
16126      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16127      * process that block using the passed callback.
16128      * @param {Object} params This parameter is not used by the MemoryProxy class.
16129      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16130      * object into a block of Roo.data.Records.
16131      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16132      * The function must be passed <ul>
16133      * <li>The Record block object</li>
16134      * <li>The "arg" argument from the load function</li>
16135      * <li>A boolean success indicator</li>
16136      * </ul>
16137      * @param {Object} scope The scope in which to call the callback
16138      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16139      */
16140     load : function(params, reader, callback, scope, arg){
16141         params = params || {};
16142         var result;
16143         try {
16144             result = reader.readRecords(params.data ? params.data :this.data);
16145         }catch(e){
16146             this.fireEvent("loadexception", this, arg, null, e);
16147             callback.call(scope, null, arg, false);
16148             return;
16149         }
16150         callback.call(scope, result, arg, true);
16151     },
16152     
16153     // private
16154     update : function(params, records){
16155         
16156     }
16157 });/*
16158  * Based on:
16159  * Ext JS Library 1.1.1
16160  * Copyright(c) 2006-2007, Ext JS, LLC.
16161  *
16162  * Originally Released Under LGPL - original licence link has changed is not relivant.
16163  *
16164  * Fork - LGPL
16165  * <script type="text/javascript">
16166  */
16167 /**
16168  * @class Roo.data.HttpProxy
16169  * @extends Roo.data.DataProxy
16170  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16171  * configured to reference a certain URL.<br><br>
16172  * <p>
16173  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16174  * from which the running page was served.<br><br>
16175  * <p>
16176  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16177  * <p>
16178  * Be aware that to enable the browser to parse an XML document, the server must set
16179  * the Content-Type header in the HTTP response to "text/xml".
16180  * @constructor
16181  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16182  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16183  * will be used to make the request.
16184  */
16185 Roo.data.HttpProxy = function(conn){
16186     Roo.data.HttpProxy.superclass.constructor.call(this);
16187     // is conn a conn config or a real conn?
16188     this.conn = conn;
16189     this.useAjax = !conn || !conn.events;
16190   
16191 };
16192
16193 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16194     // thse are take from connection...
16195     
16196     /**
16197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16198      */
16199     /**
16200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16201      * extra parameters to each request made by this object. (defaults to undefined)
16202      */
16203     /**
16204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16205      *  to each request made by this object. (defaults to undefined)
16206      */
16207     /**
16208      * @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)
16209      */
16210     /**
16211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16212      */
16213      /**
16214      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16215      * @type Boolean
16216      */
16217   
16218
16219     /**
16220      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16221      * @type Boolean
16222      */
16223     /**
16224      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16225      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16226      * a finer-grained basis than the DataProxy events.
16227      */
16228     getConnection : function(){
16229         return this.useAjax ? Roo.Ajax : this.conn;
16230     },
16231
16232     /**
16233      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16234      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16235      * process that block using the passed callback.
16236      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16237      * for the request to the remote server.
16238      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16239      * object into a block of Roo.data.Records.
16240      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16241      * The function must be passed <ul>
16242      * <li>The Record block object</li>
16243      * <li>The "arg" argument from the load function</li>
16244      * <li>A boolean success indicator</li>
16245      * </ul>
16246      * @param {Object} scope The scope in which to call the callback
16247      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16248      */
16249     load : function(params, reader, callback, scope, arg){
16250         if(this.fireEvent("beforeload", this, params) !== false){
16251             var  o = {
16252                 params : params || {},
16253                 request: {
16254                     callback : callback,
16255                     scope : scope,
16256                     arg : arg
16257                 },
16258                 reader: reader,
16259                 callback : this.loadResponse,
16260                 scope: this
16261             };
16262             if(this.useAjax){
16263                 Roo.applyIf(o, this.conn);
16264                 if(this.activeRequest){
16265                     Roo.Ajax.abort(this.activeRequest);
16266                 }
16267                 this.activeRequest = Roo.Ajax.request(o);
16268             }else{
16269                 this.conn.request(o);
16270             }
16271         }else{
16272             callback.call(scope||this, null, arg, false);
16273         }
16274     },
16275
16276     // private
16277     loadResponse : function(o, success, response){
16278         delete this.activeRequest;
16279         if(!success){
16280             this.fireEvent("loadexception", this, o, response);
16281             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16282             return;
16283         }
16284         var result;
16285         try {
16286             result = o.reader.read(response);
16287         }catch(e){
16288             o.success = false;
16289             o.raw = { errorMsg : response.responseText };
16290             this.fireEvent("loadexception", this, o, response, e);
16291             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16292             return;
16293         }
16294         
16295         this.fireEvent("load", this, o, o.request.arg);
16296         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16297     },
16298
16299     // private
16300     update : function(dataSet){
16301
16302     },
16303
16304     // private
16305     updateResponse : function(dataSet){
16306
16307     }
16308 });/*
16309  * Based on:
16310  * Ext JS Library 1.1.1
16311  * Copyright(c) 2006-2007, Ext JS, LLC.
16312  *
16313  * Originally Released Under LGPL - original licence link has changed is not relivant.
16314  *
16315  * Fork - LGPL
16316  * <script type="text/javascript">
16317  */
16318
16319 /**
16320  * @class Roo.data.ScriptTagProxy
16321  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16322  * other than the originating domain of the running page.<br><br>
16323  * <p>
16324  * <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
16325  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16326  * <p>
16327  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16328  * source code that is used as the source inside a &lt;script> tag.<br><br>
16329  * <p>
16330  * In order for the browser to process the returned data, the server must wrap the data object
16331  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16332  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16333  * depending on whether the callback name was passed:
16334  * <p>
16335  * <pre><code>
16336 boolean scriptTag = false;
16337 String cb = request.getParameter("callback");
16338 if (cb != null) {
16339     scriptTag = true;
16340     response.setContentType("text/javascript");
16341 } else {
16342     response.setContentType("application/x-json");
16343 }
16344 Writer out = response.getWriter();
16345 if (scriptTag) {
16346     out.write(cb + "(");
16347 }
16348 out.print(dataBlock.toJsonString());
16349 if (scriptTag) {
16350     out.write(");");
16351 }
16352 </pre></code>
16353  *
16354  * @constructor
16355  * @param {Object} config A configuration object.
16356  */
16357 Roo.data.ScriptTagProxy = function(config){
16358     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16359     Roo.apply(this, config);
16360     this.head = document.getElementsByTagName("head")[0];
16361 };
16362
16363 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16364
16365 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16366     /**
16367      * @cfg {String} url The URL from which to request the data object.
16368      */
16369     /**
16370      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16371      */
16372     timeout : 30000,
16373     /**
16374      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16375      * the server the name of the callback function set up by the load call to process the returned data object.
16376      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16377      * javascript output which calls this named function passing the data object as its only parameter.
16378      */
16379     callbackParam : "callback",
16380     /**
16381      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16382      * name to the request.
16383      */
16384     nocache : true,
16385
16386     /**
16387      * Load data from the configured URL, read the data object into
16388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16389      * process that block using the passed callback.
16390      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16391      * for the request to the remote server.
16392      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16393      * object into a block of Roo.data.Records.
16394      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16395      * The function must be passed <ul>
16396      * <li>The Record block object</li>
16397      * <li>The "arg" argument from the load function</li>
16398      * <li>A boolean success indicator</li>
16399      * </ul>
16400      * @param {Object} scope The scope in which to call the callback
16401      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16402      */
16403     load : function(params, reader, callback, scope, arg){
16404         if(this.fireEvent("beforeload", this, params) !== false){
16405
16406             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16407
16408             var url = this.url;
16409             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16410             if(this.nocache){
16411                 url += "&_dc=" + (new Date().getTime());
16412             }
16413             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16414             var trans = {
16415                 id : transId,
16416                 cb : "stcCallback"+transId,
16417                 scriptId : "stcScript"+transId,
16418                 params : params,
16419                 arg : arg,
16420                 url : url,
16421                 callback : callback,
16422                 scope : scope,
16423                 reader : reader
16424             };
16425             var conn = this;
16426
16427             window[trans.cb] = function(o){
16428                 conn.handleResponse(o, trans);
16429             };
16430
16431             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16432
16433             if(this.autoAbort !== false){
16434                 this.abort();
16435             }
16436
16437             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16438
16439             var script = document.createElement("script");
16440             script.setAttribute("src", url);
16441             script.setAttribute("type", "text/javascript");
16442             script.setAttribute("id", trans.scriptId);
16443             this.head.appendChild(script);
16444
16445             this.trans = trans;
16446         }else{
16447             callback.call(scope||this, null, arg, false);
16448         }
16449     },
16450
16451     // private
16452     isLoading : function(){
16453         return this.trans ? true : false;
16454     },
16455
16456     /**
16457      * Abort the current server request.
16458      */
16459     abort : function(){
16460         if(this.isLoading()){
16461             this.destroyTrans(this.trans);
16462         }
16463     },
16464
16465     // private
16466     destroyTrans : function(trans, isLoaded){
16467         this.head.removeChild(document.getElementById(trans.scriptId));
16468         clearTimeout(trans.timeoutId);
16469         if(isLoaded){
16470             window[trans.cb] = undefined;
16471             try{
16472                 delete window[trans.cb];
16473             }catch(e){}
16474         }else{
16475             // if hasn't been loaded, wait for load to remove it to prevent script error
16476             window[trans.cb] = function(){
16477                 window[trans.cb] = undefined;
16478                 try{
16479                     delete window[trans.cb];
16480                 }catch(e){}
16481             };
16482         }
16483     },
16484
16485     // private
16486     handleResponse : function(o, trans){
16487         this.trans = false;
16488         this.destroyTrans(trans, true);
16489         var result;
16490         try {
16491             result = trans.reader.readRecords(o);
16492         }catch(e){
16493             this.fireEvent("loadexception", this, o, trans.arg, e);
16494             trans.callback.call(trans.scope||window, null, trans.arg, false);
16495             return;
16496         }
16497         this.fireEvent("load", this, o, trans.arg);
16498         trans.callback.call(trans.scope||window, result, trans.arg, true);
16499     },
16500
16501     // private
16502     handleFailure : function(trans){
16503         this.trans = false;
16504         this.destroyTrans(trans, false);
16505         this.fireEvent("loadexception", this, null, trans.arg);
16506         trans.callback.call(trans.scope||window, null, trans.arg, false);
16507     }
16508 });/*
16509  * Based on:
16510  * Ext JS Library 1.1.1
16511  * Copyright(c) 2006-2007, Ext JS, LLC.
16512  *
16513  * Originally Released Under LGPL - original licence link has changed is not relivant.
16514  *
16515  * Fork - LGPL
16516  * <script type="text/javascript">
16517  */
16518
16519 /**
16520  * @class Roo.data.JsonReader
16521  * @extends Roo.data.DataReader
16522  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16523  * based on mappings in a provided Roo.data.Record constructor.
16524  * 
16525  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16526  * in the reply previously. 
16527  * 
16528  * <p>
16529  * Example code:
16530  * <pre><code>
16531 var RecordDef = Roo.data.Record.create([
16532     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16533     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16534 ]);
16535 var myReader = new Roo.data.JsonReader({
16536     totalProperty: "results",    // The property which contains the total dataset size (optional)
16537     root: "rows",                // The property which contains an Array of row objects
16538     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16539 }, RecordDef);
16540 </code></pre>
16541  * <p>
16542  * This would consume a JSON file like this:
16543  * <pre><code>
16544 { 'results': 2, 'rows': [
16545     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16546     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16547 }
16548 </code></pre>
16549  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16550  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16551  * paged from the remote server.
16552  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16553  * @cfg {String} root name of the property which contains the Array of row objects.
16554  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16555  * @cfg {Array} fields Array of field definition objects
16556  * @constructor
16557  * Create a new JsonReader
16558  * @param {Object} meta Metadata configuration options
16559  * @param {Object} recordType Either an Array of field definition objects,
16560  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16561  */
16562 Roo.data.JsonReader = function(meta, recordType){
16563     
16564     meta = meta || {};
16565     // set some defaults:
16566     Roo.applyIf(meta, {
16567         totalProperty: 'total',
16568         successProperty : 'success',
16569         root : 'data',
16570         id : 'id'
16571     });
16572     
16573     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16574 };
16575 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16576     
16577     readerType : 'Json',
16578     
16579     /**
16580      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16581      * Used by Store query builder to append _requestMeta to params.
16582      * 
16583      */
16584     metaFromRemote : false,
16585     /**
16586      * This method is only used by a DataProxy which has retrieved data from a remote server.
16587      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16588      * @return {Object} data A data block which is used by an Roo.data.Store object as
16589      * a cache of Roo.data.Records.
16590      */
16591     read : function(response){
16592         var json = response.responseText;
16593        
16594         var o = /* eval:var:o */ eval("("+json+")");
16595         if(!o) {
16596             throw {message: "JsonReader.read: Json object not found"};
16597         }
16598         
16599         if(o.metaData){
16600             
16601             delete this.ef;
16602             this.metaFromRemote = true;
16603             this.meta = o.metaData;
16604             this.recordType = Roo.data.Record.create(o.metaData.fields);
16605             this.onMetaChange(this.meta, this.recordType, o);
16606         }
16607         return this.readRecords(o);
16608     },
16609
16610     // private function a store will implement
16611     onMetaChange : function(meta, recordType, o){
16612
16613     },
16614
16615     /**
16616          * @ignore
16617          */
16618     simpleAccess: function(obj, subsc) {
16619         return obj[subsc];
16620     },
16621
16622         /**
16623          * @ignore
16624          */
16625     getJsonAccessor: function(){
16626         var re = /[\[\.]/;
16627         return function(expr) {
16628             try {
16629                 return(re.test(expr))
16630                     ? new Function("obj", "return obj." + expr)
16631                     : function(obj){
16632                         return obj[expr];
16633                     };
16634             } catch(e){}
16635             return Roo.emptyFn;
16636         };
16637     }(),
16638
16639     /**
16640      * Create a data block containing Roo.data.Records from an XML document.
16641      * @param {Object} o An object which contains an Array of row objects in the property specified
16642      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16643      * which contains the total size of the dataset.
16644      * @return {Object} data A data block which is used by an Roo.data.Store object as
16645      * a cache of Roo.data.Records.
16646      */
16647     readRecords : function(o){
16648         /**
16649          * After any data loads, the raw JSON data is available for further custom processing.
16650          * @type Object
16651          */
16652         this.o = o;
16653         var s = this.meta, Record = this.recordType,
16654             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16655
16656 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16657         if (!this.ef) {
16658             if(s.totalProperty) {
16659                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16660                 }
16661                 if(s.successProperty) {
16662                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16663                 }
16664                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16665                 if (s.id) {
16666                         var g = this.getJsonAccessor(s.id);
16667                         this.getId = function(rec) {
16668                                 var r = g(rec);  
16669                                 return (r === undefined || r === "") ? null : r;
16670                         };
16671                 } else {
16672                         this.getId = function(){return null;};
16673                 }
16674             this.ef = [];
16675             for(var jj = 0; jj < fl; jj++){
16676                 f = fi[jj];
16677                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16678                 this.ef[jj] = this.getJsonAccessor(map);
16679             }
16680         }
16681
16682         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16683         if(s.totalProperty){
16684             var vt = parseInt(this.getTotal(o), 10);
16685             if(!isNaN(vt)){
16686                 totalRecords = vt;
16687             }
16688         }
16689         if(s.successProperty){
16690             var vs = this.getSuccess(o);
16691             if(vs === false || vs === 'false'){
16692                 success = false;
16693             }
16694         }
16695         var records = [];
16696         for(var i = 0; i < c; i++){
16697             var n = root[i];
16698             var values = {};
16699             var id = this.getId(n);
16700             for(var j = 0; j < fl; j++){
16701                 f = fi[j];
16702                                 var v = this.ef[j](n);
16703                                 if (!f.convert) {
16704                                         Roo.log('missing convert for ' + f.name);
16705                                         Roo.log(f);
16706                                         continue;
16707                                 }
16708                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16709             }
16710                         if (!Record) {
16711                                 return {
16712                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16713                                         success : false,
16714                                         records : [],
16715                                         totalRecords : 0
16716                                 };
16717                         }
16718             var record = new Record(values, id);
16719             record.json = n;
16720             records[i] = record;
16721         }
16722         return {
16723             raw : o,
16724             success : success,
16725             records : records,
16726             totalRecords : totalRecords
16727         };
16728     },
16729     // used when loading children.. @see loadDataFromChildren
16730     toLoadData: function(rec)
16731     {
16732         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16733         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16734         return { data : data, total : data.length };
16735         
16736     }
16737 });/*
16738  * Based on:
16739  * Ext JS Library 1.1.1
16740  * Copyright(c) 2006-2007, Ext JS, LLC.
16741  *
16742  * Originally Released Under LGPL - original licence link has changed is not relivant.
16743  *
16744  * Fork - LGPL
16745  * <script type="text/javascript">
16746  */
16747
16748 /**
16749  * @class Roo.data.ArrayReader
16750  * @extends Roo.data.DataReader
16751  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16752  * Each element of that Array represents a row of data fields. The
16753  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16754  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16755  * <p>
16756  * Example code:.
16757  * <pre><code>
16758 var RecordDef = Roo.data.Record.create([
16759     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16760     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16761 ]);
16762 var myReader = new Roo.data.ArrayReader({
16763     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16764 }, RecordDef);
16765 </code></pre>
16766  * <p>
16767  * This would consume an Array like this:
16768  * <pre><code>
16769 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16770   </code></pre>
16771  
16772  * @constructor
16773  * Create a new JsonReader
16774  * @param {Object} meta Metadata configuration options.
16775  * @param {Object|Array} recordType Either an Array of field definition objects
16776  * 
16777  * @cfg {Array} fields Array of field definition objects
16778  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16779  * as specified to {@link Roo.data.Record#create},
16780  * or an {@link Roo.data.Record} object
16781  *
16782  * 
16783  * created using {@link Roo.data.Record#create}.
16784  */
16785 Roo.data.ArrayReader = function(meta, recordType)
16786 {    
16787     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16788 };
16789
16790 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16791     
16792       /**
16793      * Create a data block containing Roo.data.Records from an XML document.
16794      * @param {Object} o An Array of row objects which represents the dataset.
16795      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16796      * a cache of Roo.data.Records.
16797      */
16798     readRecords : function(o)
16799     {
16800         var sid = this.meta ? this.meta.id : null;
16801         var recordType = this.recordType, fields = recordType.prototype.fields;
16802         var records = [];
16803         var root = o;
16804         for(var i = 0; i < root.length; i++){
16805             var n = root[i];
16806             var values = {};
16807             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16808             for(var j = 0, jlen = fields.length; j < jlen; j++){
16809                 var f = fields.items[j];
16810                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16811                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16812                 v = f.convert(v);
16813                 values[f.name] = v;
16814             }
16815             var record = new recordType(values, id);
16816             record.json = n;
16817             records[records.length] = record;
16818         }
16819         return {
16820             records : records,
16821             totalRecords : records.length
16822         };
16823     },
16824     // used when loading children.. @see loadDataFromChildren
16825     toLoadData: function(rec)
16826     {
16827         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16828         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16829         
16830     }
16831     
16832     
16833 });/*
16834  * - LGPL
16835  * * 
16836  */
16837
16838 /**
16839  * @class Roo.bootstrap.form.ComboBox
16840  * @extends Roo.bootstrap.form.TriggerField
16841  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16842  * @cfg {Boolean} append (true|false) default false
16843  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16844  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16845  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16846  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16847  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16848  * @cfg {Boolean} animate default true
16849  * @cfg {Boolean} emptyResultText only for touch device
16850  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16851  * @cfg {String} emptyTitle default ''
16852  * @cfg {Number} width fixed with? experimental
16853  * @constructor
16854  * Create a new ComboBox.
16855  * @param {Object} config Configuration options
16856  */
16857 Roo.bootstrap.form.ComboBox = function(config){
16858     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16859     this.addEvents({
16860         /**
16861          * @event expand
16862          * Fires when the dropdown list is expanded
16863         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864         */
16865         'expand' : true,
16866         /**
16867          * @event collapse
16868          * Fires when the dropdown list is collapsed
16869         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870         */
16871         'collapse' : true,
16872         /**
16873          * @event beforeselect
16874          * Fires before a list item is selected. Return false to cancel the selection.
16875         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876         * @param {Roo.data.Record} record The data record returned from the underlying store
16877         * @param {Number} index The index of the selected item in the dropdown list
16878         */
16879         'beforeselect' : true,
16880         /**
16881          * @event select
16882          * Fires when a list item is selected
16883         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16884         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16885         * @param {Number} index The index of the selected item in the dropdown list
16886         */
16887         'select' : true,
16888         /**
16889          * @event beforequery
16890          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16891          * The event object passed has these properties:
16892         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16893         * @param {String} query The query
16894         * @param {Boolean} forceAll true to force "all" query
16895         * @param {Boolean} cancel true to cancel the query
16896         * @param {Object} e The query event object
16897         */
16898         'beforequery': true,
16899          /**
16900          * @event add
16901          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16902         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16903         */
16904         'add' : true,
16905         /**
16906          * @event edit
16907          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16910         */
16911         'edit' : true,
16912         /**
16913          * @event remove
16914          * Fires when the remove value from the combobox array
16915         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16916         */
16917         'remove' : true,
16918         /**
16919          * @event afterremove
16920          * Fires when the remove value from the combobox array
16921         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16922         */
16923         'afterremove' : true,
16924         /**
16925          * @event specialfilter
16926          * Fires when specialfilter
16927             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16928             */
16929         'specialfilter' : true,
16930         /**
16931          * @event tick
16932          * Fires when tick the element
16933             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16934             */
16935         'tick' : true,
16936         /**
16937          * @event touchviewdisplay
16938          * Fires when touch view require special display (default is using displayField)
16939             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16940             * @param {Object} cfg set html .
16941             */
16942         'touchviewdisplay' : true
16943         
16944     });
16945     
16946     this.item = [];
16947     this.tickItems = [];
16948     
16949     this.selectedIndex = -1;
16950     if(this.mode == 'local'){
16951         if(config.queryDelay === undefined){
16952             this.queryDelay = 10;
16953         }
16954         if(config.minChars === undefined){
16955             this.minChars = 0;
16956         }
16957     }
16958 };
16959
16960 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16961      
16962     /**
16963      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16964      * rendering into an Roo.Editor, defaults to false)
16965      */
16966     /**
16967      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16968      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16969      */
16970     /**
16971      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16972      */
16973     /**
16974      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16975      * the dropdown list (defaults to undefined, with no header element)
16976      */
16977
16978      /**
16979      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16980      */
16981      
16982      /**
16983      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16984      */
16985     listWidth: undefined,
16986     /**
16987      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16988      * mode = 'remote' or 'text' if mode = 'local')
16989      */
16990     displayField: undefined,
16991     
16992     /**
16993      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16994      * mode = 'remote' or 'value' if mode = 'local'). 
16995      * Note: use of a valueField requires the user make a selection
16996      * in order for a value to be mapped.
16997      */
16998     valueField: undefined,
16999     /**
17000      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17001      */
17002     modalTitle : '',
17003     
17004     /**
17005      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17006      * field's data value (defaults to the underlying DOM element's name)
17007      */
17008     hiddenName: undefined,
17009     /**
17010      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17011      */
17012     listClass: '',
17013     /**
17014      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17015      */
17016     selectedClass: 'active',
17017     
17018     /**
17019      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17020      */
17021     shadow:'sides',
17022     /**
17023      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17024      * anchor positions (defaults to 'tl-bl')
17025      */
17026     listAlign: 'tl-bl?',
17027     /**
17028      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17029      */
17030     maxHeight: 300,
17031     /**
17032      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17033      * query specified by the allQuery config option (defaults to 'query')
17034      */
17035     triggerAction: 'query',
17036     /**
17037      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17038      * (defaults to 4, does not apply if editable = false)
17039      */
17040     minChars : 4,
17041     /**
17042      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17043      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17044      */
17045     typeAhead: false,
17046     /**
17047      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17048      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17049      */
17050     queryDelay: 500,
17051     /**
17052      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17053      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17054      */
17055     pageSize: 0,
17056     /**
17057      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17058      * when editable = true (defaults to false)
17059      */
17060     selectOnFocus:false,
17061     /**
17062      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17063      */
17064     queryParam: 'query',
17065     /**
17066      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17067      * when mode = 'remote' (defaults to 'Loading...')
17068      */
17069     loadingText: 'Loading...',
17070     /**
17071      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17072      */
17073     resizable: false,
17074     /**
17075      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17076      */
17077     handleHeight : 8,
17078     /**
17079      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17080      * traditional select (defaults to true)
17081      */
17082     editable: true,
17083     /**
17084      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17085      */
17086     allQuery: '',
17087     /**
17088      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17089      */
17090     mode: 'remote',
17091     /**
17092      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17093      * listWidth has a higher value)
17094      */
17095     minListWidth : 70,
17096     /**
17097      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17098      * allow the user to set arbitrary text into the field (defaults to false)
17099      */
17100     forceSelection:false,
17101     /**
17102      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17103      * if typeAhead = true (defaults to 250)
17104      */
17105     typeAheadDelay : 250,
17106     /**
17107      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17108      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17109      */
17110     valueNotFoundText : undefined,
17111     /**
17112      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17113      */
17114     blockFocus : false,
17115     
17116     /**
17117      * @cfg {Boolean} disableClear Disable showing of clear button.
17118      */
17119     disableClear : false,
17120     /**
17121      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17122      */
17123     alwaysQuery : false,
17124     
17125     /**
17126      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17127      */
17128     multiple : false,
17129     
17130     /**
17131      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17132      */
17133     invalidClass : "has-warning",
17134     
17135     /**
17136      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17137      */
17138     validClass : "has-success",
17139     
17140     /**
17141      * @cfg {Boolean} specialFilter (true|false) special filter default false
17142      */
17143     specialFilter : false,
17144     
17145     /**
17146      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17147      */
17148     mobileTouchView : true,
17149     
17150     /**
17151      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17152      */
17153     useNativeIOS : false,
17154     
17155     /**
17156      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17157      */
17158     mobile_restrict_height : false,
17159     
17160     ios_options : false,
17161     
17162     //private
17163     addicon : false,
17164     editicon: false,
17165     
17166     page: 0,
17167     hasQuery: false,
17168     append: false,
17169     loadNext: false,
17170     autoFocus : true,
17171     tickable : false,
17172     btnPosition : 'right',
17173     triggerList : true,
17174     showToggleBtn : true,
17175     animate : true,
17176     emptyResultText: 'Empty',
17177     triggerText : 'Select',
17178     emptyTitle : '',
17179     width : false,
17180     
17181     // element that contains real text value.. (when hidden is used..)
17182     
17183     getAutoCreate : function()
17184     {   
17185         var cfg = false;
17186         //render
17187         /*
17188          * Render classic select for iso
17189          */
17190         
17191         if(Roo.isIOS && this.useNativeIOS){
17192             cfg = this.getAutoCreateNativeIOS();
17193             return cfg;
17194         }
17195         
17196         /*
17197          * Touch Devices
17198          */
17199         
17200         if(Roo.isTouch && this.mobileTouchView){
17201             cfg = this.getAutoCreateTouchView();
17202             return cfg;;
17203         }
17204         
17205         /*
17206          *  Normal ComboBox
17207          */
17208         if(!this.tickable){
17209             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17210             return cfg;
17211         }
17212         
17213         /*
17214          *  ComboBox with tickable selections
17215          */
17216              
17217         var align = this.labelAlign || this.parentLabelAlign();
17218         
17219         cfg = {
17220             cls : 'form-group roo-combobox-tickable' //input-group
17221         };
17222         
17223         var btn_text_select = '';
17224         var btn_text_done = '';
17225         var btn_text_cancel = '';
17226         
17227         if (this.btn_text_show) {
17228             btn_text_select = 'Select';
17229             btn_text_done = 'Done';
17230             btn_text_cancel = 'Cancel'; 
17231         }
17232         
17233         var buttons = {
17234             tag : 'div',
17235             cls : 'tickable-buttons',
17236             cn : [
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17241                     //html : this.triggerText
17242                     html: btn_text_select
17243                 },
17244                 {
17245                     tag : 'button',
17246                     type : 'button',
17247                     name : 'ok',
17248                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17249                     //html : 'Done'
17250                     html: btn_text_done
17251                 },
17252                 {
17253                     tag : 'button',
17254                     type : 'button',
17255                     name : 'cancel',
17256                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17257                     //html : 'Cancel'
17258                     html: btn_text_cancel
17259                 }
17260             ]
17261         };
17262         
17263         if(this.editable){
17264             buttons.cn.unshift({
17265                 tag: 'input',
17266                 cls: 'roo-select2-search-field-input'
17267             });
17268         }
17269         
17270         var _this = this;
17271         
17272         Roo.each(buttons.cn, function(c){
17273             if (_this.size) {
17274                 c.cls += ' btn-' + _this.size;
17275             }
17276
17277             if (_this.disabled) {
17278                 c.disabled = true;
17279             }
17280         });
17281         
17282         var box = {
17283             tag: 'div',
17284             style : 'display: contents',
17285             cn: [
17286                 {
17287                     tag: 'input',
17288                     type : 'hidden',
17289                     cls: 'form-hidden-field'
17290                 },
17291                 {
17292                     tag: 'ul',
17293                     cls: 'roo-select2-choices',
17294                     cn:[
17295                         {
17296                             tag: 'li',
17297                             cls: 'roo-select2-search-field',
17298                             cn: [
17299                                 buttons
17300                             ]
17301                         }
17302                     ]
17303                 }
17304             ]
17305         };
17306         
17307         var combobox = {
17308             cls: 'roo-select2-container input-group roo-select2-container-multi',
17309             cn: [
17310                 
17311                 box
17312 //                {
17313 //                    tag: 'ul',
17314 //                    cls: 'typeahead typeahead-long dropdown-menu',
17315 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17316 //                }
17317             ]
17318         };
17319         
17320         if(this.hasFeedback && !this.allowBlank){
17321             
17322             var feedback = {
17323                 tag: 'span',
17324                 cls: 'glyphicon form-control-feedback'
17325             };
17326
17327             combobox.cn.push(feedback);
17328         }
17329         
17330         
17331         
17332         var indicator = {
17333             tag : 'i',
17334             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17335             tooltip : 'This field is required'
17336         };
17337         if (Roo.bootstrap.version == 4) {
17338             indicator = {
17339                 tag : 'i',
17340                 style : 'display:none'
17341             };
17342         }
17343         if (align ==='left' && this.fieldLabel.length) {
17344             
17345             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17346             
17347             cfg.cn = [
17348                 indicator,
17349                 {
17350                     tag: 'label',
17351                     'for' :  id,
17352                     cls : 'control-label col-form-label',
17353                     html : this.fieldLabel
17354
17355                 },
17356                 {
17357                     cls : "", 
17358                     cn: [
17359                         combobox
17360                     ]
17361                 }
17362
17363             ];
17364             
17365             var labelCfg = cfg.cn[1];
17366             var contentCfg = cfg.cn[2];
17367             
17368
17369             if(this.indicatorpos == 'right'){
17370                 
17371                 cfg.cn = [
17372                     {
17373                         tag: 'label',
17374                         'for' :  id,
17375                         cls : 'control-label col-form-label',
17376                         cn : [
17377                             {
17378                                 tag : 'span',
17379                                 html : this.fieldLabel
17380                             },
17381                             indicator
17382                         ]
17383                     },
17384                     {
17385                         cls : "",
17386                         cn: [
17387                             combobox
17388                         ]
17389                     }
17390
17391                 ];
17392                 
17393                 
17394                 
17395                 labelCfg = cfg.cn[0];
17396                 contentCfg = cfg.cn[1];
17397             
17398             }
17399             
17400             if(this.labelWidth > 12){
17401                 labelCfg.style = "width: " + this.labelWidth + 'px';
17402             }
17403             if(this.width * 1 > 0){
17404                 contentCfg.style = "width: " + this.width + 'px';
17405             }
17406             if(this.labelWidth < 13 && this.labelmd == 0){
17407                 this.labelmd = this.labelWidth;
17408             }
17409             
17410             if(this.labellg > 0){
17411                 labelCfg.cls += ' col-lg-' + this.labellg;
17412                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17413             }
17414             
17415             if(this.labelmd > 0){
17416                 labelCfg.cls += ' col-md-' + this.labelmd;
17417                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17418             }
17419             
17420             if(this.labelsm > 0){
17421                 labelCfg.cls += ' col-sm-' + this.labelsm;
17422                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17423             }
17424             
17425             if(this.labelxs > 0){
17426                 labelCfg.cls += ' col-xs-' + this.labelxs;
17427                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17428             }
17429                 
17430                 
17431         } else if ( this.fieldLabel.length) {
17432 //                Roo.log(" label");
17433                  cfg.cn = [
17434                    indicator,
17435                     {
17436                         tag: 'label',
17437                         //cls : 'input-group-addon',
17438                         html : this.fieldLabel
17439                     },
17440                     combobox
17441                 ];
17442                 
17443                 if(this.indicatorpos == 'right'){
17444                     cfg.cn = [
17445                         {
17446                             tag: 'label',
17447                             //cls : 'input-group-addon',
17448                             html : this.fieldLabel
17449                         },
17450                         indicator,
17451                         combobox
17452                     ];
17453                     
17454                 }
17455
17456         } else {
17457             
17458 //                Roo.log(" no label && no align");
17459                 cfg = combobox
17460                      
17461                 
17462         }
17463          
17464         var settings=this;
17465         ['xs','sm','md','lg'].map(function(size){
17466             if (settings[size]) {
17467                 cfg.cls += ' col-' + size + '-' + settings[size];
17468             }
17469         });
17470         
17471         return cfg;
17472         
17473     },
17474     
17475     _initEventsCalled : false,
17476     
17477     // private
17478     initEvents: function()
17479     {   
17480         if (this._initEventsCalled) { // as we call render... prevent looping...
17481             return;
17482         }
17483         this._initEventsCalled = true;
17484         
17485         if (!this.store) {
17486             throw "can not find store for combo";
17487         }
17488         
17489         this.indicator = this.indicatorEl();
17490         
17491         this.store = Roo.factory(this.store, Roo.data);
17492         this.store.parent = this;
17493         
17494         // if we are building from html. then this element is so complex, that we can not really
17495         // use the rendered HTML.
17496         // so we have to trash and replace the previous code.
17497         if (Roo.XComponent.build_from_html) {
17498             // remove this element....
17499             var e = this.el.dom, k=0;
17500             while (e ) { e = e.previousSibling;  ++k;}
17501
17502             this.el.remove();
17503             
17504             this.el=false;
17505             this.rendered = false;
17506             
17507             this.render(this.parent().getChildContainer(true), k);
17508         }
17509         
17510         if(Roo.isIOS && this.useNativeIOS){
17511             this.initIOSView();
17512             return;
17513         }
17514         
17515         /*
17516          * Touch Devices
17517          */
17518         
17519         if(Roo.isTouch && this.mobileTouchView){
17520             this.initTouchView();
17521             return;
17522         }
17523         
17524         if(this.tickable){
17525             this.initTickableEvents();
17526             return;
17527         }
17528         
17529         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17530         
17531         if(this.hiddenName){
17532             
17533             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17534             
17535             this.hiddenField.dom.value =
17536                 this.hiddenValue !== undefined ? this.hiddenValue :
17537                 this.value !== undefined ? this.value : '';
17538
17539             // prevent input submission
17540             this.el.dom.removeAttribute('name');
17541             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17542              
17543              
17544         }
17545         //if(Roo.isGecko){
17546         //    this.el.dom.setAttribute('autocomplete', 'off');
17547         //}
17548         
17549         var cls = 'x-combo-list';
17550         
17551         //this.list = new Roo.Layer({
17552         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17553         //});
17554         
17555         var _this = this;
17556         
17557         (function(){
17558             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17559             _this.list.setWidth(lw);
17560         }).defer(100);
17561         
17562         this.list.on('mouseover', this.onViewOver, this);
17563         this.list.on('mousemove', this.onViewMove, this);
17564         this.list.on('scroll', this.onViewScroll, this);
17565         
17566         /*
17567         this.list.swallowEvent('mousewheel');
17568         this.assetHeight = 0;
17569
17570         if(this.title){
17571             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17572             this.assetHeight += this.header.getHeight();
17573         }
17574
17575         this.innerList = this.list.createChild({cls:cls+'-inner'});
17576         this.innerList.on('mouseover', this.onViewOver, this);
17577         this.innerList.on('mousemove', this.onViewMove, this);
17578         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17579         
17580         if(this.allowBlank && !this.pageSize && !this.disableClear){
17581             this.footer = this.list.createChild({cls:cls+'-ft'});
17582             this.pageTb = new Roo.Toolbar(this.footer);
17583            
17584         }
17585         if(this.pageSize){
17586             this.footer = this.list.createChild({cls:cls+'-ft'});
17587             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17588                     {pageSize: this.pageSize});
17589             
17590         }
17591         
17592         if (this.pageTb && this.allowBlank && !this.disableClear) {
17593             var _this = this;
17594             this.pageTb.add(new Roo.Toolbar.Fill(), {
17595                 cls: 'x-btn-icon x-btn-clear',
17596                 text: '&#160;',
17597                 handler: function()
17598                 {
17599                     _this.collapse();
17600                     _this.clearValue();
17601                     _this.onSelect(false, -1);
17602                 }
17603             });
17604         }
17605         if (this.footer) {
17606             this.assetHeight += this.footer.getHeight();
17607         }
17608         */
17609             
17610         if(!this.tpl){
17611             this.tpl = Roo.bootstrap.version == 4 ?
17612                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17613                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17614         }
17615
17616         this.view = new Roo.View(this.list, this.tpl, {
17617             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17618         });
17619         //this.view.wrapEl.setDisplayed(false);
17620         this.view.on('click', this.onViewClick, this);
17621         
17622         
17623         this.store.on('beforeload', this.onBeforeLoad, this);
17624         this.store.on('load', this.onLoad, this);
17625         this.store.on('loadexception', this.onLoadException, this);
17626         /*
17627         if(this.resizable){
17628             this.resizer = new Roo.Resizable(this.list,  {
17629                pinned:true, handles:'se'
17630             });
17631             this.resizer.on('resize', function(r, w, h){
17632                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17633                 this.listWidth = w;
17634                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17635                 this.restrictHeight();
17636             }, this);
17637             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17638         }
17639         */
17640         if(!this.editable){
17641             this.editable = true;
17642             this.setEditable(false);
17643         }
17644         
17645         /*
17646         
17647         if (typeof(this.events.add.listeners) != 'undefined') {
17648             
17649             this.addicon = this.wrap.createChild(
17650                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17651        
17652             this.addicon.on('click', function(e) {
17653                 this.fireEvent('add', this);
17654             }, this);
17655         }
17656         if (typeof(this.events.edit.listeners) != 'undefined') {
17657             
17658             this.editicon = this.wrap.createChild(
17659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17660             if (this.addicon) {
17661                 this.editicon.setStyle('margin-left', '40px');
17662             }
17663             this.editicon.on('click', function(e) {
17664                 
17665                 // we fire even  if inothing is selected..
17666                 this.fireEvent('edit', this, this.lastData );
17667                 
17668             }, this);
17669         }
17670         */
17671         
17672         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17673             "up" : function(e){
17674                 this.inKeyMode = true;
17675                 this.selectPrev();
17676             },
17677
17678             "down" : function(e){
17679                 if(!this.isExpanded()){
17680                     this.onTriggerClick();
17681                 }else{
17682                     this.inKeyMode = true;
17683                     this.selectNext();
17684                 }
17685             },
17686
17687             "enter" : function(e){
17688 //                this.onViewClick();
17689                 //return true;
17690                 this.collapse();
17691                 
17692                 if(this.fireEvent("specialkey", this, e)){
17693                     this.onViewClick(false);
17694                 }
17695                 
17696                 return true;
17697             },
17698
17699             "esc" : function(e){
17700                 this.collapse();
17701             },
17702
17703             "tab" : function(e){
17704                 this.collapse();
17705                 
17706                 if(this.fireEvent("specialkey", this, e)){
17707                     this.onViewClick(false);
17708                 }
17709                 
17710                 return true;
17711             },
17712
17713             scope : this,
17714
17715             doRelay : function(foo, bar, hname){
17716                 if(hname == 'down' || this.scope.isExpanded()){
17717                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17718                 }
17719                 return true;
17720             },
17721
17722             forceKeyDown: true
17723         });
17724         
17725         
17726         this.queryDelay = Math.max(this.queryDelay || 10,
17727                 this.mode == 'local' ? 10 : 250);
17728         
17729         
17730         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17731         
17732         if(this.typeAhead){
17733             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17734         }
17735         if(this.editable !== false){
17736             this.inputEl().on("keyup", this.onKeyUp, this);
17737         }
17738         if(this.forceSelection){
17739             this.inputEl().on('blur', this.doForce, this);
17740         }
17741         
17742         if(this.multiple){
17743             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17744             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17745         }
17746     },
17747     
17748     initTickableEvents: function()
17749     {   
17750         this.createList();
17751         
17752         if(this.hiddenName){
17753             
17754             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17755             
17756             this.hiddenField.dom.value =
17757                 this.hiddenValue !== undefined ? this.hiddenValue :
17758                 this.value !== undefined ? this.value : '';
17759
17760             // prevent input submission
17761             this.el.dom.removeAttribute('name');
17762             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17763              
17764              
17765         }
17766         
17767 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17768         
17769         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17770         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17771         if(this.triggerList){
17772             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17773         }
17774          
17775         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17776         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17777         
17778         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17779         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17780         
17781         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17782         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17783         
17784         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17785         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17786         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17787         
17788         this.okBtn.hide();
17789         this.cancelBtn.hide();
17790         
17791         var _this = this;
17792         
17793         (function(){
17794             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17795             _this.list.setWidth(lw);
17796         }).defer(100);
17797         
17798         this.list.on('mouseover', this.onViewOver, this);
17799         this.list.on('mousemove', this.onViewMove, this);
17800         
17801         this.list.on('scroll', this.onViewScroll, this);
17802         
17803         if(!this.tpl){
17804             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17805                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17806         }
17807
17808         this.view = new Roo.View(this.list, this.tpl, {
17809             singleSelect:true,
17810             tickable:true,
17811             parent:this,
17812             store: this.store,
17813             selectedClass: this.selectedClass
17814         });
17815         
17816         //this.view.wrapEl.setDisplayed(false);
17817         this.view.on('click', this.onViewClick, this);
17818         
17819         
17820         
17821         this.store.on('beforeload', this.onBeforeLoad, this);
17822         this.store.on('load', this.onLoad, this);
17823         this.store.on('loadexception', this.onLoadException, this);
17824         
17825         if(this.editable){
17826             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17827                 "up" : function(e){
17828                     this.inKeyMode = true;
17829                     this.selectPrev();
17830                 },
17831
17832                 "down" : function(e){
17833                     this.inKeyMode = true;
17834                     this.selectNext();
17835                 },
17836
17837                 "enter" : function(e){
17838                     if(this.fireEvent("specialkey", this, e)){
17839                         this.onViewClick(false);
17840                     }
17841                     
17842                     return true;
17843                 },
17844
17845                 "esc" : function(e){
17846                     this.onTickableFooterButtonClick(e, false, false);
17847                 },
17848
17849                 "tab" : function(e){
17850                     this.fireEvent("specialkey", this, e);
17851                     
17852                     this.onTickableFooterButtonClick(e, false, false);
17853                     
17854                     return true;
17855                 },
17856
17857                 scope : this,
17858
17859                 doRelay : function(e, fn, key){
17860                     if(this.scope.isExpanded()){
17861                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17862                     }
17863                     return true;
17864                 },
17865
17866                 forceKeyDown: true
17867             });
17868         }
17869         
17870         this.queryDelay = Math.max(this.queryDelay || 10,
17871                 this.mode == 'local' ? 10 : 250);
17872         
17873         
17874         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17875         
17876         if(this.typeAhead){
17877             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17878         }
17879         
17880         if(this.editable !== false){
17881             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17882         }
17883         
17884         this.indicator = this.indicatorEl();
17885         
17886         if(this.indicator){
17887             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17888             this.indicator.hide();
17889         }
17890         
17891     },
17892
17893     onDestroy : function(){
17894         if(this.view){
17895             this.view.setStore(null);
17896             this.view.el.removeAllListeners();
17897             this.view.el.remove();
17898             this.view.purgeListeners();
17899         }
17900         if(this.list){
17901             this.list.dom.innerHTML  = '';
17902         }
17903         
17904         if(this.store){
17905             this.store.un('beforeload', this.onBeforeLoad, this);
17906             this.store.un('load', this.onLoad, this);
17907             this.store.un('loadexception', this.onLoadException, this);
17908         }
17909         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17910     },
17911
17912     // private
17913     fireKey : function(e){
17914         if(e.isNavKeyPress() && !this.list.isVisible()){
17915             this.fireEvent("specialkey", this, e);
17916         }
17917     },
17918
17919     // private
17920     onResize: function(w, h)
17921     {
17922         
17923         
17924 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17925 //        
17926 //        if(typeof w != 'number'){
17927 //            // we do not handle it!?!?
17928 //            return;
17929 //        }
17930 //        var tw = this.trigger.getWidth();
17931 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17932 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17933 //        var x = w - tw;
17934 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17935 //            
17936 //        //this.trigger.setStyle('left', x+'px');
17937 //        
17938 //        if(this.list && this.listWidth === undefined){
17939 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17940 //            this.list.setWidth(lw);
17941 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17942 //        }
17943         
17944     
17945         
17946     },
17947
17948     /**
17949      * Allow or prevent the user from directly editing the field text.  If false is passed,
17950      * the user will only be able to select from the items defined in the dropdown list.  This method
17951      * is the runtime equivalent of setting the 'editable' config option at config time.
17952      * @param {Boolean} value True to allow the user to directly edit the field text
17953      */
17954     setEditable : function(value){
17955         if(value == this.editable){
17956             return;
17957         }
17958         this.editable = value;
17959         if(!value){
17960             this.inputEl().dom.setAttribute('readOnly', true);
17961             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17962             this.inputEl().addClass('x-combo-noedit');
17963         }else{
17964             this.inputEl().dom.removeAttribute('readOnly');
17965             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17966             this.inputEl().removeClass('x-combo-noedit');
17967         }
17968     },
17969
17970     // private
17971     
17972     onBeforeLoad : function(combo,opts){
17973         if(!this.hasFocus){
17974             return;
17975         }
17976          if (!opts.add) {
17977             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17978          }
17979         this.restrictHeight();
17980         this.selectedIndex = -1;
17981     },
17982
17983     // private
17984     onLoad : function(){
17985         
17986         this.hasQuery = false;
17987         
17988         if(!this.hasFocus){
17989             return;
17990         }
17991         
17992         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17993             this.loading.hide();
17994         }
17995         
17996         if(this.store.getCount() > 0){
17997             
17998             this.expand();
17999             this.restrictHeight();
18000             if(this.lastQuery == this.allQuery){
18001                 if(this.editable && !this.tickable){
18002                     this.inputEl().dom.select();
18003                 }
18004                 
18005                 if(
18006                     !this.selectByValue(this.value, true) &&
18007                     this.autoFocus && 
18008                     (
18009                         !this.store.lastOptions ||
18010                         typeof(this.store.lastOptions.add) == 'undefined' || 
18011                         this.store.lastOptions.add != true
18012                     )
18013                 ){
18014                     this.select(0, true);
18015                 }
18016             }else{
18017                 if(this.autoFocus){
18018                     this.selectNext();
18019                 }
18020                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18021                     this.taTask.delay(this.typeAheadDelay);
18022                 }
18023             }
18024         }else{
18025             this.onEmptyResults();
18026         }
18027         
18028         //this.el.focus();
18029     },
18030     // private
18031     onLoadException : function()
18032     {
18033         this.hasQuery = false;
18034         
18035         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18036             this.loading.hide();
18037         }
18038         
18039         if(this.tickable && this.editable){
18040             return;
18041         }
18042         
18043         this.collapse();
18044         // only causes errors at present
18045         //Roo.log(this.store.reader.jsonData);
18046         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18047             // fixme
18048             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18049         //}
18050         
18051         
18052     },
18053     // private
18054     onTypeAhead : function(){
18055         if(this.store.getCount() > 0){
18056             var r = this.store.getAt(0);
18057             var newValue = r.data[this.displayField];
18058             var len = newValue.length;
18059             var selStart = this.getRawValue().length;
18060             
18061             if(selStart != len){
18062                 this.setRawValue(newValue);
18063                 this.selectText(selStart, newValue.length);
18064             }
18065         }
18066     },
18067
18068     // private
18069     onSelect : function(record, index){
18070         
18071         if(this.fireEvent('beforeselect', this, record, index) !== false){
18072         
18073             this.setFromData(index > -1 ? record.data : false);
18074             
18075             this.collapse();
18076             this.fireEvent('select', this, record, index);
18077         }
18078     },
18079
18080     /**
18081      * Returns the currently selected field value or empty string if no value is set.
18082      * @return {String} value The selected value
18083      */
18084     getValue : function()
18085     {
18086         if(Roo.isIOS && this.useNativeIOS){
18087             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18088         }
18089         
18090         if(this.multiple){
18091             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18092         }
18093         
18094         if(this.valueField){
18095             return typeof this.value != 'undefined' ? this.value : '';
18096         }else{
18097             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18098         }
18099     },
18100     
18101     getRawValue : function()
18102     {
18103         if(Roo.isIOS && this.useNativeIOS){
18104             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18105         }
18106         
18107         var v = this.inputEl().getValue();
18108         
18109         return v;
18110     },
18111
18112     /**
18113      * Clears any text/value currently set in the field
18114      */
18115     clearValue : function(){
18116         
18117         if(this.hiddenField){
18118             this.hiddenField.dom.value = '';
18119         }
18120         this.value = '';
18121         this.setRawValue('');
18122         this.lastSelectionText = '';
18123         this.lastData = false;
18124         
18125         var close = this.closeTriggerEl();
18126         
18127         if(close){
18128             close.hide();
18129         }
18130         
18131         this.validate();
18132         
18133     },
18134
18135     /**
18136      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18137      * will be displayed in the field.  If the value does not match the data value of an existing item,
18138      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18139      * Otherwise the field will be blank (although the value will still be set).
18140      * @param {String} value The value to match
18141      */
18142     setValue : function(v)
18143     {
18144         if(Roo.isIOS && this.useNativeIOS){
18145             this.setIOSValue(v);
18146             return;
18147         }
18148         
18149         if(this.multiple){
18150             this.syncValue();
18151             return;
18152         }
18153         
18154         var text = v;
18155         if(this.valueField){
18156             var r = this.findRecord(this.valueField, v);
18157             if(r){
18158                 text = r.data[this.displayField];
18159             }else if(this.valueNotFoundText !== undefined){
18160                 text = this.valueNotFoundText;
18161             }
18162         }
18163         this.lastSelectionText = text;
18164         if(this.hiddenField){
18165             this.hiddenField.dom.value = v;
18166         }
18167         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18168         this.value = v;
18169         
18170         var close = this.closeTriggerEl();
18171         
18172         if(close){
18173             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18174         }
18175         
18176         this.validate();
18177     },
18178     /**
18179      * @property {Object} the last set data for the element
18180      */
18181     
18182     lastData : false,
18183     /**
18184      * Sets the value of the field based on a object which is related to the record format for the store.
18185      * @param {Object} value the value to set as. or false on reset?
18186      */
18187     setFromData : function(o){
18188         
18189         if(this.multiple){
18190             this.addItem(o);
18191             return;
18192         }
18193             
18194         var dv = ''; // display value
18195         var vv = ''; // value value..
18196         this.lastData = o;
18197         if (this.displayField) {
18198             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18199         } else {
18200             // this is an error condition!!!
18201             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18202         }
18203         
18204         if(this.valueField){
18205             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18206         }
18207         
18208         var close = this.closeTriggerEl();
18209         
18210         if(close){
18211             if(dv.length || vv * 1 > 0){
18212                 close.show() ;
18213                 this.blockFocus=true;
18214             } else {
18215                 close.hide();
18216             }             
18217         }
18218         
18219         if(this.hiddenField){
18220             this.hiddenField.dom.value = vv;
18221             
18222             this.lastSelectionText = dv;
18223             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224             this.value = vv;
18225             return;
18226         }
18227         // no hidden field.. - we store the value in 'value', but still display
18228         // display field!!!!
18229         this.lastSelectionText = dv;
18230         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18231         this.value = vv;
18232         
18233         
18234         
18235     },
18236     // private
18237     reset : function(){
18238         // overridden so that last data is reset..
18239         
18240         if(this.multiple){
18241             this.clearItem();
18242             return;
18243         }
18244         
18245         this.setValue(this.originalValue);
18246         //this.clearInvalid();
18247         this.lastData = false;
18248         if (this.view) {
18249             this.view.clearSelections();
18250         }
18251         
18252         this.validate();
18253     },
18254     // private
18255     findRecord : function(prop, value){
18256         var record;
18257         if(this.store.getCount() > 0){
18258             this.store.each(function(r){
18259                 if(r.data[prop] == value){
18260                     record = r;
18261                     return false;
18262                 }
18263                 return true;
18264             });
18265         }
18266         return record;
18267     },
18268     
18269     getName: function()
18270     {
18271         // returns hidden if it's set..
18272         if (!this.rendered) {return ''};
18273         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18274         
18275     },
18276     // private
18277     onViewMove : function(e, t){
18278         this.inKeyMode = false;
18279     },
18280
18281     // private
18282     onViewOver : function(e, t){
18283         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18284             return;
18285         }
18286         var item = this.view.findItemFromChild(t);
18287         
18288         if(item){
18289             var index = this.view.indexOf(item);
18290             this.select(index, false);
18291         }
18292     },
18293
18294     // private
18295     onViewClick : function(view, doFocus, el, e)
18296     {
18297         var index = this.view.getSelectedIndexes()[0];
18298         
18299         var r = this.store.getAt(index);
18300         
18301         if(this.tickable){
18302             
18303             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18304                 return;
18305             }
18306             
18307             var rm = false;
18308             var _this = this;
18309             
18310             Roo.each(this.tickItems, function(v,k){
18311                 
18312                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18313                     Roo.log(v);
18314                     _this.tickItems.splice(k, 1);
18315                     
18316                     if(typeof(e) == 'undefined' && view == false){
18317                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18318                     }
18319                     
18320                     rm = true;
18321                     return;
18322                 }
18323             });
18324             
18325             if(rm){
18326                 return;
18327             }
18328             
18329             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18330                 this.tickItems.push(r.data);
18331             }
18332             
18333             if(typeof(e) == 'undefined' && view == false){
18334                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18335             }
18336                     
18337             return;
18338         }
18339         
18340         if(r){
18341             this.onSelect(r, index);
18342         }
18343         if(doFocus !== false && !this.blockFocus){
18344             this.inputEl().focus();
18345         }
18346     },
18347
18348     // private
18349     restrictHeight : function(){
18350         //this.innerList.dom.style.height = '';
18351         //var inner = this.innerList.dom;
18352         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18353         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18354         //this.list.beginUpdate();
18355         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18356         this.list.alignTo(this.inputEl(), this.listAlign);
18357         this.list.alignTo(this.inputEl(), this.listAlign);
18358         //this.list.endUpdate();
18359     },
18360
18361     // private
18362     onEmptyResults : function(){
18363         
18364         if(this.tickable && this.editable){
18365             this.hasFocus = false;
18366             this.restrictHeight();
18367             return;
18368         }
18369         
18370         this.collapse();
18371     },
18372
18373     /**
18374      * Returns true if the dropdown list is expanded, else false.
18375      */
18376     isExpanded : function(){
18377         return this.list.isVisible();
18378     },
18379
18380     /**
18381      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18382      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18383      * @param {String} value The data value of the item to select
18384      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18385      * selected item if it is not currently in view (defaults to true)
18386      * @return {Boolean} True if the value matched an item in the list, else false
18387      */
18388     selectByValue : function(v, scrollIntoView){
18389         if(v !== undefined && v !== null){
18390             var r = this.findRecord(this.valueField || this.displayField, v);
18391             if(r){
18392                 this.select(this.store.indexOf(r), scrollIntoView);
18393                 return true;
18394             }
18395         }
18396         return false;
18397     },
18398
18399     /**
18400      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18401      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18402      * @param {Number} index The zero-based index of the list item to select
18403      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18404      * selected item if it is not currently in view (defaults to true)
18405      */
18406     select : function(index, scrollIntoView){
18407         this.selectedIndex = index;
18408         this.view.select(index);
18409         if(scrollIntoView !== false){
18410             var el = this.view.getNode(index);
18411             /*
18412              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18413              */
18414             if(el){
18415                 this.list.scrollChildIntoView(el, false);
18416             }
18417         }
18418     },
18419
18420     // private
18421     selectNext : function(){
18422         var ct = this.store.getCount();
18423         if(ct > 0){
18424             if(this.selectedIndex == -1){
18425                 this.select(0);
18426             }else if(this.selectedIndex < ct-1){
18427                 this.select(this.selectedIndex+1);
18428             }
18429         }
18430     },
18431
18432     // private
18433     selectPrev : function(){
18434         var ct = this.store.getCount();
18435         if(ct > 0){
18436             if(this.selectedIndex == -1){
18437                 this.select(0);
18438             }else if(this.selectedIndex != 0){
18439                 this.select(this.selectedIndex-1);
18440             }
18441         }
18442     },
18443
18444     // private
18445     onKeyUp : function(e){
18446         if(this.editable !== false && !e.isSpecialKey()){
18447             this.lastKey = e.getKey();
18448             this.dqTask.delay(this.queryDelay);
18449         }
18450     },
18451
18452     // private
18453     validateBlur : function(){
18454         return !this.list || !this.list.isVisible();   
18455     },
18456
18457     // private
18458     initQuery : function(){
18459         
18460         var v = this.getRawValue();
18461         
18462         if(this.tickable && this.editable){
18463             v = this.tickableInputEl().getValue();
18464         }
18465         
18466         this.doQuery(v);
18467     },
18468
18469     // private
18470     doForce : function(){
18471         if(this.inputEl().dom.value.length > 0){
18472             this.inputEl().dom.value =
18473                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18474              
18475         }
18476     },
18477
18478     /**
18479      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18480      * query allowing the query action to be canceled if needed.
18481      * @param {String} query The SQL query to execute
18482      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18483      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18484      * saved in the current store (defaults to false)
18485      */
18486     doQuery : function(q, forceAll){
18487         
18488         if(q === undefined || q === null){
18489             q = '';
18490         }
18491         var qe = {
18492             query: q,
18493             forceAll: forceAll,
18494             combo: this,
18495             cancel:false
18496         };
18497         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18498             return false;
18499         }
18500         q = qe.query;
18501         
18502         forceAll = qe.forceAll;
18503         if(forceAll === true || (q.length >= this.minChars)){
18504             
18505             this.hasQuery = true;
18506             
18507             if(this.lastQuery != q || this.alwaysQuery){
18508                 this.lastQuery = q;
18509                 if(this.mode == 'local'){
18510                     this.selectedIndex = -1;
18511                     if(forceAll){
18512                         this.store.clearFilter();
18513                     }else{
18514                         
18515                         if(this.specialFilter){
18516                             this.fireEvent('specialfilter', this);
18517                             this.onLoad();
18518                             return;
18519                         }
18520                         
18521                         this.store.filter(this.displayField, q);
18522                     }
18523                     
18524                     this.store.fireEvent("datachanged", this.store);
18525                     
18526                     this.onLoad();
18527                     
18528                     
18529                 }else{
18530                     
18531                     this.store.baseParams[this.queryParam] = q;
18532                     
18533                     var options = {params : this.getParams(q)};
18534                     
18535                     if(this.loadNext){
18536                         options.add = true;
18537                         options.params.start = this.page * this.pageSize;
18538                     }
18539                     
18540                     this.store.load(options);
18541                     
18542                     /*
18543                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18544                      *  we should expand the list on onLoad
18545                      *  so command out it
18546                      */
18547 //                    this.expand();
18548                 }
18549             }else{
18550                 this.selectedIndex = -1;
18551                 this.onLoad();   
18552             }
18553         }
18554         
18555         this.loadNext = false;
18556     },
18557     
18558     // private
18559     getParams : function(q){
18560         var p = {};
18561         //p[this.queryParam] = q;
18562         
18563         if(this.pageSize){
18564             p.start = 0;
18565             p.limit = this.pageSize;
18566         }
18567         return p;
18568     },
18569
18570     /**
18571      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18572      */
18573     collapse : function(){
18574         if(!this.isExpanded()){
18575             return;
18576         }
18577         
18578         this.list.hide();
18579         
18580         this.hasFocus = false;
18581         
18582         if(this.tickable){
18583             this.okBtn.hide();
18584             this.cancelBtn.hide();
18585             this.trigger.show();
18586             
18587             if(this.editable){
18588                 this.tickableInputEl().dom.value = '';
18589                 this.tickableInputEl().blur();
18590             }
18591             
18592         }
18593         
18594         Roo.get(document).un('mousedown', this.collapseIf, this);
18595         Roo.get(document).un('mousewheel', this.collapseIf, this);
18596         if (!this.editable) {
18597             Roo.get(document).un('keydown', this.listKeyPress, this);
18598         }
18599         this.fireEvent('collapse', this);
18600         
18601         this.validate();
18602     },
18603
18604     // private
18605     collapseIf : function(e){
18606         var in_combo  = e.within(this.el);
18607         var in_list =  e.within(this.list);
18608         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18609         
18610         if (in_combo || in_list || is_list) {
18611             //e.stopPropagation();
18612             return;
18613         }
18614         
18615         if(this.tickable){
18616             this.onTickableFooterButtonClick(e, false, false);
18617         }
18618
18619         this.collapse();
18620         
18621     },
18622
18623     /**
18624      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18625      */
18626     expand : function(){
18627        
18628         if(this.isExpanded() || !this.hasFocus){
18629             return;
18630         }
18631         
18632         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18633         this.list.setWidth(lw);
18634         
18635         Roo.log('expand');
18636         
18637         this.list.show();
18638         
18639         this.restrictHeight();
18640         
18641         if(this.tickable){
18642             
18643             this.tickItems = Roo.apply([], this.item);
18644             
18645             this.okBtn.show();
18646             this.cancelBtn.show();
18647             this.trigger.hide();
18648             
18649             if(this.editable){
18650                 this.tickableInputEl().focus();
18651             }
18652             
18653         }
18654         
18655         Roo.get(document).on('mousedown', this.collapseIf, this);
18656         Roo.get(document).on('mousewheel', this.collapseIf, this);
18657         if (!this.editable) {
18658             Roo.get(document).on('keydown', this.listKeyPress, this);
18659         }
18660         
18661         this.fireEvent('expand', this);
18662     },
18663
18664     // private
18665     // Implements the default empty TriggerField.onTriggerClick function
18666     onTriggerClick : function(e)
18667     {
18668         Roo.log('trigger click');
18669         
18670         if(this.disabled || !this.triggerList){
18671             return;
18672         }
18673         
18674         this.page = 0;
18675         this.loadNext = false;
18676         
18677         if(this.isExpanded()){
18678             this.collapse();
18679             if (!this.blockFocus) {
18680                 this.inputEl().focus();
18681             }
18682             
18683         }else {
18684             this.hasFocus = true;
18685             if(this.triggerAction == 'all') {
18686                 this.doQuery(this.allQuery, true);
18687             } else {
18688                 this.doQuery(this.getRawValue());
18689             }
18690             if (!this.blockFocus) {
18691                 this.inputEl().focus();
18692             }
18693         }
18694     },
18695     
18696     onTickableTriggerClick : function(e)
18697     {
18698         if(this.disabled){
18699             return;
18700         }
18701         
18702         this.page = 0;
18703         this.loadNext = false;
18704         this.hasFocus = true;
18705         
18706         if(this.triggerAction == 'all') {
18707             this.doQuery(this.allQuery, true);
18708         } else {
18709             this.doQuery(this.getRawValue());
18710         }
18711     },
18712     
18713     onSearchFieldClick : function(e)
18714     {
18715         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18716             this.onTickableFooterButtonClick(e, false, false);
18717             return;
18718         }
18719         
18720         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18721             return;
18722         }
18723         
18724         this.page = 0;
18725         this.loadNext = false;
18726         this.hasFocus = true;
18727         
18728         if(this.triggerAction == 'all') {
18729             this.doQuery(this.allQuery, true);
18730         } else {
18731             this.doQuery(this.getRawValue());
18732         }
18733     },
18734     
18735     listKeyPress : function(e)
18736     {
18737         //Roo.log('listkeypress');
18738         // scroll to first matching element based on key pres..
18739         if (e.isSpecialKey()) {
18740             return false;
18741         }
18742         var k = String.fromCharCode(e.getKey()).toUpperCase();
18743         //Roo.log(k);
18744         var match  = false;
18745         var csel = this.view.getSelectedNodes();
18746         var cselitem = false;
18747         if (csel.length) {
18748             var ix = this.view.indexOf(csel[0]);
18749             cselitem  = this.store.getAt(ix);
18750             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18751                 cselitem = false;
18752             }
18753             
18754         }
18755         
18756         this.store.each(function(v) { 
18757             if (cselitem) {
18758                 // start at existing selection.
18759                 if (cselitem.id == v.id) {
18760                     cselitem = false;
18761                 }
18762                 return true;
18763             }
18764                 
18765             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18766                 match = this.store.indexOf(v);
18767                 return false;
18768             }
18769             return true;
18770         }, this);
18771         
18772         if (match === false) {
18773             return true; // no more action?
18774         }
18775         // scroll to?
18776         this.view.select(match);
18777         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18778         sn.scrollIntoView(sn.dom.parentNode, false);
18779     },
18780     
18781     onViewScroll : function(e, t){
18782         
18783         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){
18784             return;
18785         }
18786         
18787         this.hasQuery = true;
18788         
18789         this.loading = this.list.select('.loading', true).first();
18790         
18791         if(this.loading === null){
18792             this.list.createChild({
18793                 tag: 'div',
18794                 cls: 'loading roo-select2-more-results roo-select2-active',
18795                 html: 'Loading more results...'
18796             });
18797             
18798             this.loading = this.list.select('.loading', true).first();
18799             
18800             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18801             
18802             this.loading.hide();
18803         }
18804         
18805         this.loading.show();
18806         
18807         var _combo = this;
18808         
18809         this.page++;
18810         this.loadNext = true;
18811         
18812         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18813         
18814         return;
18815     },
18816     
18817     addItem : function(o)
18818     {   
18819         var dv = ''; // display value
18820         
18821         if (this.displayField) {
18822             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18823         } else {
18824             // this is an error condition!!!
18825             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18826         }
18827         
18828         if(!dv.length){
18829             return;
18830         }
18831         
18832         var choice = this.choices.createChild({
18833             tag: 'li',
18834             cls: 'roo-select2-search-choice',
18835             cn: [
18836                 {
18837                     tag: 'div',
18838                     html: dv
18839                 },
18840                 {
18841                     tag: 'a',
18842                     href: '#',
18843                     cls: 'roo-select2-search-choice-close fa fa-times',
18844                     tabindex: '-1'
18845                 }
18846             ]
18847             
18848         }, this.searchField);
18849         
18850         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18851         
18852         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18853         
18854         this.item.push(o);
18855         
18856         this.lastData = o;
18857         
18858         this.syncValue();
18859         
18860         this.inputEl().dom.value = '';
18861         
18862         this.validate();
18863     },
18864     
18865     onRemoveItem : function(e, _self, o)
18866     {
18867         e.preventDefault();
18868         
18869         this.lastItem = Roo.apply([], this.item);
18870         
18871         var index = this.item.indexOf(o.data) * 1;
18872         
18873         if( index < 0){
18874             Roo.log('not this item?!');
18875             return;
18876         }
18877         
18878         this.item.splice(index, 1);
18879         o.item.remove();
18880         
18881         this.syncValue();
18882         
18883         this.fireEvent('remove', this, e);
18884         
18885         this.validate();
18886         
18887     },
18888     
18889     syncValue : function()
18890     {
18891         if(!this.item.length){
18892             this.clearValue();
18893             return;
18894         }
18895             
18896         var value = [];
18897         var _this = this;
18898         Roo.each(this.item, function(i){
18899             if(_this.valueField){
18900                 value.push(i[_this.valueField]);
18901                 return;
18902             }
18903
18904             value.push(i);
18905         });
18906
18907         this.value = value.join(',');
18908
18909         if(this.hiddenField){
18910             this.hiddenField.dom.value = this.value;
18911         }
18912         
18913         this.store.fireEvent("datachanged", this.store);
18914         
18915         this.validate();
18916     },
18917     
18918     clearItem : function()
18919     {
18920         if(!this.multiple){
18921             return;
18922         }
18923         
18924         this.item = [];
18925         
18926         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18927            c.remove();
18928         });
18929         
18930         this.syncValue();
18931         
18932         this.validate();
18933         
18934         if(this.tickable && !Roo.isTouch){
18935             this.view.refresh();
18936         }
18937     },
18938     
18939     inputEl: function ()
18940     {
18941         if(Roo.isIOS && this.useNativeIOS){
18942             return this.el.select('select.roo-ios-select', true).first();
18943         }
18944         
18945         if(Roo.isTouch && this.mobileTouchView){
18946             return this.el.select('input.form-control',true).first();
18947         }
18948         
18949         if(this.tickable){
18950             return this.searchField;
18951         }
18952         
18953         return this.el.select('input.form-control',true).first();
18954     },
18955     
18956     onTickableFooterButtonClick : function(e, btn, el)
18957     {
18958         e.preventDefault();
18959         
18960         this.lastItem = Roo.apply([], this.item);
18961         
18962         if(btn && btn.name == 'cancel'){
18963             this.tickItems = Roo.apply([], this.item);
18964             this.collapse();
18965             return;
18966         }
18967         
18968         this.clearItem();
18969         
18970         var _this = this;
18971         
18972         Roo.each(this.tickItems, function(o){
18973             _this.addItem(o);
18974         });
18975         
18976         this.collapse();
18977         
18978     },
18979     
18980     validate : function()
18981     {
18982         if(this.getVisibilityEl().hasClass('hidden')){
18983             return true;
18984         }
18985         
18986         var v = this.getRawValue();
18987         
18988         if(this.multiple){
18989             v = this.getValue();
18990         }
18991         
18992         if(this.disabled || this.allowBlank || v.length){
18993             this.markValid();
18994             return true;
18995         }
18996         
18997         this.markInvalid();
18998         return false;
18999     },
19000     
19001     tickableInputEl : function()
19002     {
19003         if(!this.tickable || !this.editable){
19004             return this.inputEl();
19005         }
19006         
19007         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19008     },
19009     
19010     
19011     getAutoCreateTouchView : function()
19012     {
19013         var id = Roo.id();
19014         
19015         var cfg = {
19016             cls: 'form-group' //input-group
19017         };
19018         
19019         var input =  {
19020             tag: 'input',
19021             id : id,
19022             type : this.inputType,
19023             cls : 'form-control x-combo-noedit',
19024             autocomplete: 'new-password',
19025             placeholder : this.placeholder || '',
19026             readonly : true
19027         };
19028         
19029         if (this.name) {
19030             input.name = this.name;
19031         }
19032         
19033         if (this.size) {
19034             input.cls += ' input-' + this.size;
19035         }
19036         
19037         if (this.disabled) {
19038             input.disabled = true;
19039         }
19040         
19041         var inputblock = {
19042             cls : 'roo-combobox-wrap',
19043             cn : [
19044                 input
19045             ]
19046         };
19047         
19048         if(this.before){
19049             inputblock.cls += ' input-group';
19050             
19051             inputblock.cn.unshift({
19052                 tag :'span',
19053                 cls : 'input-group-addon input-group-prepend input-group-text',
19054                 html : this.before
19055             });
19056         }
19057         
19058         if(this.removable && !this.multiple){
19059             inputblock.cls += ' roo-removable';
19060             
19061             inputblock.cn.push({
19062                 tag: 'button',
19063                 html : 'x',
19064                 cls : 'roo-combo-removable-btn close'
19065             });
19066         }
19067
19068         if(this.hasFeedback && !this.allowBlank){
19069             
19070             inputblock.cls += ' has-feedback';
19071             
19072             inputblock.cn.push({
19073                 tag: 'span',
19074                 cls: 'glyphicon form-control-feedback'
19075             });
19076             
19077         }
19078         
19079         if (this.after) {
19080             
19081             inputblock.cls += (this.before) ? '' : ' input-group';
19082             
19083             inputblock.cn.push({
19084                 tag :'span',
19085                 cls : 'input-group-addon input-group-append input-group-text',
19086                 html : this.after
19087             });
19088         }
19089
19090         
19091         var ibwrap = inputblock;
19092         
19093         if(this.multiple){
19094             ibwrap = {
19095                 tag: 'ul',
19096                 cls: 'roo-select2-choices',
19097                 cn:[
19098                     {
19099                         tag: 'li',
19100                         cls: 'roo-select2-search-field',
19101                         cn: [
19102
19103                             inputblock
19104                         ]
19105                     }
19106                 ]
19107             };
19108         
19109             
19110         }
19111         
19112         var combobox = {
19113             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19114             cn: [
19115                 {
19116                     tag: 'input',
19117                     type : 'hidden',
19118                     cls: 'form-hidden-field'
19119                 },
19120                 ibwrap
19121             ]
19122         };
19123         
19124         if(!this.multiple && this.showToggleBtn){
19125             
19126             var caret = {
19127                 cls: 'caret'
19128             };
19129             
19130             if (this.caret != false) {
19131                 caret = {
19132                      tag: 'i',
19133                      cls: 'fa fa-' + this.caret
19134                 };
19135                 
19136             }
19137             
19138             combobox.cn.push({
19139                 tag :'span',
19140                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19141                 cn : [
19142                     Roo.bootstrap.version == 3 ? caret : '',
19143                     {
19144                         tag: 'span',
19145                         cls: 'combobox-clear',
19146                         cn  : [
19147                             {
19148                                 tag : 'i',
19149                                 cls: 'icon-remove'
19150                             }
19151                         ]
19152                     }
19153                 ]
19154
19155             })
19156         }
19157         
19158         if(this.multiple){
19159             combobox.cls += ' roo-select2-container-multi';
19160         }
19161         
19162         var required =  this.allowBlank ?  {
19163                     tag : 'i',
19164                     style: 'display: none'
19165                 } : {
19166                    tag : 'i',
19167                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19168                    tooltip : 'This field is required'
19169                 };
19170         
19171         var align = this.labelAlign || this.parentLabelAlign();
19172         
19173         if (align ==='left' && this.fieldLabel.length) {
19174
19175             cfg.cn = [
19176                 required,
19177                 {
19178                     tag: 'label',
19179                     cls : 'control-label col-form-label',
19180                     html : this.fieldLabel
19181
19182                 },
19183                 {
19184                     cls : 'roo-combobox-wrap ', 
19185                     cn: [
19186                         combobox
19187                     ]
19188                 }
19189             ];
19190             
19191             var labelCfg = cfg.cn[1];
19192             var contentCfg = cfg.cn[2];
19193             
19194
19195             if(this.indicatorpos == 'right'){
19196                 cfg.cn = [
19197                     {
19198                         tag: 'label',
19199                         'for' :  id,
19200                         cls : 'control-label col-form-label',
19201                         cn : [
19202                             {
19203                                 tag : 'span',
19204                                 html : this.fieldLabel
19205                             },
19206                             required
19207                         ]
19208                     },
19209                     {
19210                         cls : "roo-combobox-wrap ",
19211                         cn: [
19212                             combobox
19213                         ]
19214                     }
19215
19216                 ];
19217                 
19218                 labelCfg = cfg.cn[0];
19219                 contentCfg = cfg.cn[1];
19220             }
19221             
19222            
19223             
19224             if(this.labelWidth > 12){
19225                 labelCfg.style = "width: " + this.labelWidth + 'px';
19226             }
19227            
19228             if(this.labelWidth < 13 && this.labelmd == 0){
19229                 this.labelmd = this.labelWidth;
19230             }
19231             
19232             if(this.labellg > 0){
19233                 labelCfg.cls += ' col-lg-' + this.labellg;
19234                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19235             }
19236             
19237             if(this.labelmd > 0){
19238                 labelCfg.cls += ' col-md-' + this.labelmd;
19239                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19240             }
19241             
19242             if(this.labelsm > 0){
19243                 labelCfg.cls += ' col-sm-' + this.labelsm;
19244                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19245             }
19246             
19247             if(this.labelxs > 0){
19248                 labelCfg.cls += ' col-xs-' + this.labelxs;
19249                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19250             }
19251                 
19252                 
19253         } else if ( this.fieldLabel.length) {
19254             cfg.cn = [
19255                required,
19256                 {
19257                     tag: 'label',
19258                     cls : 'control-label',
19259                     html : this.fieldLabel
19260
19261                 },
19262                 {
19263                     cls : '', 
19264                     cn: [
19265                         combobox
19266                     ]
19267                 }
19268             ];
19269             
19270             if(this.indicatorpos == 'right'){
19271                 cfg.cn = [
19272                     {
19273                         tag: 'label',
19274                         cls : 'control-label',
19275                         html : this.fieldLabel,
19276                         cn : [
19277                             required
19278                         ]
19279                     },
19280                     {
19281                         cls : '', 
19282                         cn: [
19283                             combobox
19284                         ]
19285                     }
19286                 ];
19287             }
19288         } else {
19289             cfg.cn = combobox;    
19290         }
19291         
19292         
19293         var settings = this;
19294         
19295         ['xs','sm','md','lg'].map(function(size){
19296             if (settings[size]) {
19297                 cfg.cls += ' col-' + size + '-' + settings[size];
19298             }
19299         });
19300         
19301         return cfg;
19302     },
19303     
19304     initTouchView : function()
19305     {
19306         this.renderTouchView();
19307         
19308         this.touchViewEl.on('scroll', function(){
19309             this.el.dom.scrollTop = 0;
19310         }, this);
19311         
19312         this.originalValue = this.getValue();
19313         
19314         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19315         
19316         this.inputEl().on("click", this.showTouchView, this);
19317         if (this.triggerEl) {
19318             this.triggerEl.on("click", this.showTouchView, this);
19319         }
19320         
19321         
19322         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19323         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19324         
19325         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19326         
19327         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19328         this.store.on('load', this.onTouchViewLoad, this);
19329         this.store.on('loadexception', this.onTouchViewLoadException, this);
19330         
19331         if(this.hiddenName){
19332             
19333             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19334             
19335             this.hiddenField.dom.value =
19336                 this.hiddenValue !== undefined ? this.hiddenValue :
19337                 this.value !== undefined ? this.value : '';
19338         
19339             this.el.dom.removeAttribute('name');
19340             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19341         }
19342         
19343         if(this.multiple){
19344             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19345             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19346         }
19347         
19348         if(this.removable && !this.multiple){
19349             var close = this.closeTriggerEl();
19350             if(close){
19351                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19352                 close.on('click', this.removeBtnClick, this, close);
19353             }
19354         }
19355         /*
19356          * fix the bug in Safari iOS8
19357          */
19358         this.inputEl().on("focus", function(e){
19359             document.activeElement.blur();
19360         }, this);
19361         
19362         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19363         
19364         return;
19365         
19366         
19367     },
19368     
19369     renderTouchView : function()
19370     {
19371         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19372         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373         
19374         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19375         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376         
19377         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19378         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         this.touchViewBodyEl.setStyle('overflow', 'auto');
19380         
19381         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19382         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19383         
19384         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19385         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19386         
19387     },
19388     
19389     showTouchView : function()
19390     {
19391         if(this.disabled){
19392             return;
19393         }
19394         
19395         this.touchViewHeaderEl.hide();
19396
19397         if(this.modalTitle.length){
19398             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19399             this.touchViewHeaderEl.show();
19400         }
19401
19402         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19403         this.touchViewEl.show();
19404
19405         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19406         
19407         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19408         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19409
19410         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19411
19412         if(this.modalTitle.length){
19413             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19414         }
19415         
19416         this.touchViewBodyEl.setHeight(bodyHeight);
19417
19418         if(this.animate){
19419             var _this = this;
19420             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19421         }else{
19422             this.touchViewEl.addClass(['in','show']);
19423         }
19424         
19425         if(this._touchViewMask){
19426             Roo.get(document.body).addClass("x-body-masked");
19427             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19428             this._touchViewMask.setStyle('z-index', 10000);
19429             this._touchViewMask.addClass('show');
19430         }
19431         
19432         this.doTouchViewQuery();
19433         
19434     },
19435     
19436     hideTouchView : function()
19437     {
19438         this.touchViewEl.removeClass(['in','show']);
19439
19440         if(this.animate){
19441             var _this = this;
19442             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19443         }else{
19444             this.touchViewEl.setStyle('display', 'none');
19445         }
19446         
19447         if(this._touchViewMask){
19448             this._touchViewMask.removeClass('show');
19449             Roo.get(document.body).removeClass("x-body-masked");
19450         }
19451     },
19452     
19453     setTouchViewValue : function()
19454     {
19455         if(this.multiple){
19456             this.clearItem();
19457         
19458             var _this = this;
19459
19460             Roo.each(this.tickItems, function(o){
19461                 this.addItem(o);
19462             }, this);
19463         }
19464         
19465         this.hideTouchView();
19466     },
19467     
19468     doTouchViewQuery : function()
19469     {
19470         var qe = {
19471             query: '',
19472             forceAll: true,
19473             combo: this,
19474             cancel:false
19475         };
19476         
19477         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19478             return false;
19479         }
19480         
19481         if(!this.alwaysQuery || this.mode == 'local'){
19482             this.onTouchViewLoad();
19483             return;
19484         }
19485         
19486         this.store.load();
19487     },
19488     
19489     onTouchViewBeforeLoad : function(combo,opts)
19490     {
19491         return;
19492     },
19493
19494     // private
19495     onTouchViewLoad : function()
19496     {
19497         if(this.store.getCount() < 1){
19498             this.onTouchViewEmptyResults();
19499             return;
19500         }
19501         
19502         this.clearTouchView();
19503         
19504         var rawValue = this.getRawValue();
19505         
19506         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19507         
19508         this.tickItems = [];
19509         
19510         this.store.data.each(function(d, rowIndex){
19511             var row = this.touchViewListGroup.createChild(template);
19512             
19513             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19514                 row.addClass(d.data.cls);
19515             }
19516             
19517             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19518                 var cfg = {
19519                     data : d.data,
19520                     html : d.data[this.displayField]
19521                 };
19522                 
19523                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19524                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19525                 }
19526             }
19527             row.removeClass('selected');
19528             if(!this.multiple && this.valueField &&
19529                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19530             {
19531                 // radio buttons..
19532                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19533                 row.addClass('selected');
19534             }
19535             
19536             if(this.multiple && this.valueField &&
19537                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19538             {
19539                 
19540                 // checkboxes...
19541                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19542                 this.tickItems.push(d.data);
19543             }
19544             
19545             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19546             
19547         }, this);
19548         
19549         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19550         
19551         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19552
19553         if(this.modalTitle.length){
19554             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19555         }
19556
19557         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19558         
19559         if(this.mobile_restrict_height && listHeight < bodyHeight){
19560             this.touchViewBodyEl.setHeight(listHeight);
19561         }
19562         
19563         var _this = this;
19564         
19565         if(firstChecked && listHeight > bodyHeight){
19566             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19567         }
19568         
19569     },
19570     
19571     onTouchViewLoadException : function()
19572     {
19573         this.hideTouchView();
19574     },
19575     
19576     onTouchViewEmptyResults : function()
19577     {
19578         this.clearTouchView();
19579         
19580         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19581         
19582         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19583         
19584     },
19585     
19586     clearTouchView : function()
19587     {
19588         this.touchViewListGroup.dom.innerHTML = '';
19589     },
19590     
19591     onTouchViewClick : function(e, el, o)
19592     {
19593         e.preventDefault();
19594         
19595         var row = o.row;
19596         var rowIndex = o.rowIndex;
19597         
19598         var r = this.store.getAt(rowIndex);
19599         
19600         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19601             
19602             if(!this.multiple){
19603                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19604                     c.dom.removeAttribute('checked');
19605                 }, this);
19606
19607                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19608
19609                 this.setFromData(r.data);
19610
19611                 var close = this.closeTriggerEl();
19612
19613                 if(close){
19614                     close.show();
19615                 }
19616
19617                 this.hideTouchView();
19618
19619                 this.fireEvent('select', this, r, rowIndex);
19620
19621                 return;
19622             }
19623
19624             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19625                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19626                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19627                 return;
19628             }
19629
19630             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19631             this.addItem(r.data);
19632             this.tickItems.push(r.data);
19633         }
19634     },
19635     
19636     getAutoCreateNativeIOS : function()
19637     {
19638         var cfg = {
19639             cls: 'form-group' //input-group,
19640         };
19641         
19642         var combobox =  {
19643             tag: 'select',
19644             cls : 'roo-ios-select'
19645         };
19646         
19647         if (this.name) {
19648             combobox.name = this.name;
19649         }
19650         
19651         if (this.disabled) {
19652             combobox.disabled = true;
19653         }
19654         
19655         var settings = this;
19656         
19657         ['xs','sm','md','lg'].map(function(size){
19658             if (settings[size]) {
19659                 cfg.cls += ' col-' + size + '-' + settings[size];
19660             }
19661         });
19662         
19663         cfg.cn = combobox;
19664         
19665         return cfg;
19666         
19667     },
19668     
19669     initIOSView : function()
19670     {
19671         this.store.on('load', this.onIOSViewLoad, this);
19672         
19673         return;
19674     },
19675     
19676     onIOSViewLoad : function()
19677     {
19678         if(this.store.getCount() < 1){
19679             return;
19680         }
19681         
19682         this.clearIOSView();
19683         
19684         if(this.allowBlank) {
19685             
19686             var default_text = '-- SELECT --';
19687             
19688             if(this.placeholder.length){
19689                 default_text = this.placeholder;
19690             }
19691             
19692             if(this.emptyTitle.length){
19693                 default_text += ' - ' + this.emptyTitle + ' -';
19694             }
19695             
19696             var opt = this.inputEl().createChild({
19697                 tag: 'option',
19698                 value : 0,
19699                 html : default_text
19700             });
19701             
19702             var o = {};
19703             o[this.valueField] = 0;
19704             o[this.displayField] = default_text;
19705             
19706             this.ios_options.push({
19707                 data : o,
19708                 el : opt
19709             });
19710             
19711         }
19712         
19713         this.store.data.each(function(d, rowIndex){
19714             
19715             var html = '';
19716             
19717             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19718                 html = d.data[this.displayField];
19719             }
19720             
19721             var value = '';
19722             
19723             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19724                 value = d.data[this.valueField];
19725             }
19726             
19727             var option = {
19728                 tag: 'option',
19729                 value : value,
19730                 html : html
19731             };
19732             
19733             if(this.value == d.data[this.valueField]){
19734                 option['selected'] = true;
19735             }
19736             
19737             var opt = this.inputEl().createChild(option);
19738             
19739             this.ios_options.push({
19740                 data : d.data,
19741                 el : opt
19742             });
19743             
19744         }, this);
19745         
19746         this.inputEl().on('change', function(){
19747            this.fireEvent('select', this);
19748         }, this);
19749         
19750     },
19751     
19752     clearIOSView: function()
19753     {
19754         this.inputEl().dom.innerHTML = '';
19755         
19756         this.ios_options = [];
19757     },
19758     
19759     setIOSValue: function(v)
19760     {
19761         this.value = v;
19762         
19763         if(!this.ios_options){
19764             return;
19765         }
19766         
19767         Roo.each(this.ios_options, function(opts){
19768            
19769            opts.el.dom.removeAttribute('selected');
19770            
19771            if(opts.data[this.valueField] != v){
19772                return;
19773            }
19774            
19775            opts.el.dom.setAttribute('selected', true);
19776            
19777         }, this);
19778     }
19779
19780     /** 
19781     * @cfg {Boolean} grow 
19782     * @hide 
19783     */
19784     /** 
19785     * @cfg {Number} growMin 
19786     * @hide 
19787     */
19788     /** 
19789     * @cfg {Number} growMax 
19790     * @hide 
19791     */
19792     /**
19793      * @hide
19794      * @method autoSize
19795      */
19796 });
19797
19798 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19799     
19800     header : {
19801         tag: 'div',
19802         cls: 'modal-header',
19803         cn: [
19804             {
19805                 tag: 'h4',
19806                 cls: 'modal-title'
19807             }
19808         ]
19809     },
19810     
19811     body : {
19812         tag: 'div',
19813         cls: 'modal-body',
19814         cn: [
19815             {
19816                 tag: 'ul',
19817                 cls: 'list-group'
19818             }
19819         ]
19820     },
19821     
19822     listItemRadio : {
19823         tag: 'li',
19824         cls: 'list-group-item',
19825         cn: [
19826             {
19827                 tag: 'span',
19828                 cls: 'roo-combobox-list-group-item-value'
19829             },
19830             {
19831                 tag: 'div',
19832                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19833                 cn: [
19834                     {
19835                         tag: 'input',
19836                         type: 'radio'
19837                     },
19838                     {
19839                         tag: 'label'
19840                     }
19841                 ]
19842             }
19843         ]
19844     },
19845     
19846     listItemCheckbox : {
19847         tag: 'li',
19848         cls: 'list-group-item',
19849         cn: [
19850             {
19851                 tag: 'span',
19852                 cls: 'roo-combobox-list-group-item-value'
19853             },
19854             {
19855                 tag: 'div',
19856                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19857                 cn: [
19858                     {
19859                         tag: 'input',
19860                         type: 'checkbox'
19861                     },
19862                     {
19863                         tag: 'label'
19864                     }
19865                 ]
19866             }
19867         ]
19868     },
19869     
19870     emptyResult : {
19871         tag: 'div',
19872         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19873     },
19874     
19875     footer : {
19876         tag: 'div',
19877         cls: 'modal-footer',
19878         cn: [
19879             {
19880                 tag: 'div',
19881                 cls: 'row',
19882                 cn: [
19883                     {
19884                         tag: 'div',
19885                         cls: 'col-xs-6 text-left',
19886                         cn: {
19887                             tag: 'button',
19888                             cls: 'btn btn-danger roo-touch-view-cancel',
19889                             html: 'Cancel'
19890                         }
19891                     },
19892                     {
19893                         tag: 'div',
19894                         cls: 'col-xs-6 text-right',
19895                         cn: {
19896                             tag: 'button',
19897                             cls: 'btn btn-success roo-touch-view-ok',
19898                             html: 'OK'
19899                         }
19900                     }
19901                 ]
19902             }
19903         ]
19904         
19905     }
19906 });
19907
19908 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19909     
19910     touchViewTemplate : {
19911         tag: 'div',
19912         cls: 'modal fade roo-combobox-touch-view',
19913         cn: [
19914             {
19915                 tag: 'div',
19916                 cls: 'modal-dialog',
19917                 style : 'position:fixed', // we have to fix position....
19918                 cn: [
19919                     {
19920                         tag: 'div',
19921                         cls: 'modal-content',
19922                         cn: [
19923                             Roo.bootstrap.form.ComboBox.header,
19924                             Roo.bootstrap.form.ComboBox.body,
19925                             Roo.bootstrap.form.ComboBox.footer
19926                         ]
19927                     }
19928                 ]
19929             }
19930         ]
19931     }
19932 });/*
19933  * Based on:
19934  * Ext JS Library 1.1.1
19935  * Copyright(c) 2006-2007, Ext JS, LLC.
19936  *
19937  * Originally Released Under LGPL - original licence link has changed is not relivant.
19938  *
19939  * Fork - LGPL
19940  * <script type="text/javascript">
19941  */
19942
19943 /**
19944  * @class Roo.View
19945  * @extends Roo.util.Observable
19946  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19947  * This class also supports single and multi selection modes. <br>
19948  * Create a data model bound view:
19949  <pre><code>
19950  var store = new Roo.data.Store(...);
19951
19952  var view = new Roo.View({
19953     el : "my-element",
19954     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19955  
19956     singleSelect: true,
19957     selectedClass: "ydataview-selected",
19958     store: store
19959  });
19960
19961  // listen for node click?
19962  view.on("click", function(vw, index, node, e){
19963  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19964  });
19965
19966  // load XML data
19967  dataModel.load("foobar.xml");
19968  </code></pre>
19969  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19970  * <br><br>
19971  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19972  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19973  * 
19974  * Note: old style constructor is still suported (container, template, config)
19975  * 
19976  * @constructor
19977  * Create a new View
19978  * @param {Object} config The config object
19979  * 
19980  */
19981 Roo.View = function(config, depreciated_tpl, depreciated_config){
19982     
19983     this.parent = false;
19984     
19985     if (typeof(depreciated_tpl) == 'undefined') {
19986         // new way.. - universal constructor.
19987         Roo.apply(this, config);
19988         this.el  = Roo.get(this.el);
19989     } else {
19990         // old format..
19991         this.el  = Roo.get(config);
19992         this.tpl = depreciated_tpl;
19993         Roo.apply(this, depreciated_config);
19994     }
19995     this.wrapEl  = this.el.wrap().wrap();
19996     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19997     
19998     
19999     if(typeof(this.tpl) == "string"){
20000         this.tpl = new Roo.Template(this.tpl);
20001     } else {
20002         // support xtype ctors..
20003         this.tpl = new Roo.factory(this.tpl, Roo);
20004     }
20005     
20006     
20007     this.tpl.compile();
20008     
20009     /** @private */
20010     this.addEvents({
20011         /**
20012          * @event beforeclick
20013          * Fires before a click is processed. Returns false to cancel the default action.
20014          * @param {Roo.View} this
20015          * @param {Number} index The index of the target node
20016          * @param {HTMLElement} node The target node
20017          * @param {Roo.EventObject} e The raw event object
20018          */
20019             "beforeclick" : true,
20020         /**
20021          * @event click
20022          * Fires when a template node is clicked.
20023          * @param {Roo.View} this
20024          * @param {Number} index The index of the target node
20025          * @param {HTMLElement} node The target node
20026          * @param {Roo.EventObject} e The raw event object
20027          */
20028             "click" : true,
20029         /**
20030          * @event dblclick
20031          * Fires when a template node is double clicked.
20032          * @param {Roo.View} this
20033          * @param {Number} index The index of the target node
20034          * @param {HTMLElement} node The target node
20035          * @param {Roo.EventObject} e The raw event object
20036          */
20037             "dblclick" : true,
20038         /**
20039          * @event contextmenu
20040          * Fires when a template node is right clicked.
20041          * @param {Roo.View} this
20042          * @param {Number} index The index of the target node
20043          * @param {HTMLElement} node The target node
20044          * @param {Roo.EventObject} e The raw event object
20045          */
20046             "contextmenu" : true,
20047         /**
20048          * @event selectionchange
20049          * Fires when the selected nodes change.
20050          * @param {Roo.View} this
20051          * @param {Array} selections Array of the selected nodes
20052          */
20053             "selectionchange" : true,
20054     
20055         /**
20056          * @event beforeselect
20057          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20058          * @param {Roo.View} this
20059          * @param {HTMLElement} node The node to be selected
20060          * @param {Array} selections Array of currently selected nodes
20061          */
20062             "beforeselect" : true,
20063         /**
20064          * @event preparedata
20065          * Fires on every row to render, to allow you to change the data.
20066          * @param {Roo.View} this
20067          * @param {Object} data to be rendered (change this)
20068          */
20069           "preparedata" : true
20070           
20071           
20072         });
20073
20074
20075
20076     this.el.on({
20077         "click": this.onClick,
20078         "dblclick": this.onDblClick,
20079         "contextmenu": this.onContextMenu,
20080         scope:this
20081     });
20082
20083     this.selections = [];
20084     this.nodes = [];
20085     this.cmp = new Roo.CompositeElementLite([]);
20086     if(this.store){
20087         this.store = Roo.factory(this.store, Roo.data);
20088         this.setStore(this.store, true);
20089     }
20090     
20091     if ( this.footer && this.footer.xtype) {
20092            
20093          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20094         
20095         this.footer.dataSource = this.store;
20096         this.footer.container = fctr;
20097         this.footer = Roo.factory(this.footer, Roo);
20098         fctr.insertFirst(this.el);
20099         
20100         // this is a bit insane - as the paging toolbar seems to detach the el..
20101 //        dom.parentNode.parentNode.parentNode
20102          // they get detached?
20103     }
20104     
20105     
20106     Roo.View.superclass.constructor.call(this);
20107     
20108     
20109 };
20110
20111 Roo.extend(Roo.View, Roo.util.Observable, {
20112     
20113      /**
20114      * @cfg {Roo.data.Store} store Data store to load data from.
20115      */
20116     store : false,
20117     
20118     /**
20119      * @cfg {String|Roo.Element} el The container element.
20120      */
20121     el : '',
20122     
20123     /**
20124      * @cfg {String|Roo.Template} tpl The template used by this View 
20125      */
20126     tpl : false,
20127     /**
20128      * @cfg {String} dataName the named area of the template to use as the data area
20129      *                          Works with domtemplates roo-name="name"
20130      */
20131     dataName: false,
20132     /**
20133      * @cfg {String} selectedClass The css class to add to selected nodes
20134      */
20135     selectedClass : "x-view-selected",
20136      /**
20137      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20138      */
20139     emptyText : "",
20140     
20141     /**
20142      * @cfg {String} text to display on mask (default Loading)
20143      */
20144     mask : false,
20145     /**
20146      * @cfg {Boolean} multiSelect Allow multiple selection
20147      */
20148     multiSelect : false,
20149     /**
20150      * @cfg {Boolean} singleSelect Allow single selection
20151      */
20152     singleSelect:  false,
20153     
20154     /**
20155      * @cfg {Boolean} toggleSelect - selecting 
20156      */
20157     toggleSelect : false,
20158     
20159     /**
20160      * @cfg {Boolean} tickable - selecting 
20161      */
20162     tickable : false,
20163     
20164     /**
20165      * Returns the element this view is bound to.
20166      * @return {Roo.Element}
20167      */
20168     getEl : function(){
20169         return this.wrapEl;
20170     },
20171     
20172     
20173
20174     /**
20175      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20176      */
20177     refresh : function(){
20178         //Roo.log('refresh');
20179         var t = this.tpl;
20180         
20181         // if we are using something like 'domtemplate', then
20182         // the what gets used is:
20183         // t.applySubtemplate(NAME, data, wrapping data..)
20184         // the outer template then get' applied with
20185         //     the store 'extra data'
20186         // and the body get's added to the
20187         //      roo-name="data" node?
20188         //      <span class='roo-tpl-{name}'></span> ?????
20189         
20190         
20191         
20192         this.clearSelections();
20193         this.el.update("");
20194         var html = [];
20195         var records = this.store.getRange();
20196         if(records.length < 1) {
20197             
20198             // is this valid??  = should it render a template??
20199             
20200             this.el.update(this.emptyText);
20201             return;
20202         }
20203         var el = this.el;
20204         if (this.dataName) {
20205             this.el.update(t.apply(this.store.meta)); //????
20206             el = this.el.child('.roo-tpl-' + this.dataName);
20207         }
20208         
20209         for(var i = 0, len = records.length; i < len; i++){
20210             var data = this.prepareData(records[i].data, i, records[i]);
20211             this.fireEvent("preparedata", this, data, i, records[i]);
20212             
20213             var d = Roo.apply({}, data);
20214             
20215             if(this.tickable){
20216                 Roo.apply(d, {'roo-id' : Roo.id()});
20217                 
20218                 var _this = this;
20219             
20220                 Roo.each(this.parent.item, function(item){
20221                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20222                         return;
20223                     }
20224                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20225                 });
20226             }
20227             
20228             html[html.length] = Roo.util.Format.trim(
20229                 this.dataName ?
20230                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20231                     t.apply(d)
20232             );
20233         }
20234         
20235         
20236         
20237         el.update(html.join(""));
20238         this.nodes = el.dom.childNodes;
20239         this.updateIndexes(0);
20240     },
20241     
20242
20243     /**
20244      * Function to override to reformat the data that is sent to
20245      * the template for each node.
20246      * DEPRICATED - use the preparedata event handler.
20247      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20248      * a JSON object for an UpdateManager bound view).
20249      */
20250     prepareData : function(data, index, record)
20251     {
20252         this.fireEvent("preparedata", this, data, index, record);
20253         return data;
20254     },
20255
20256     onUpdate : function(ds, record){
20257         // Roo.log('on update');   
20258         this.clearSelections();
20259         var index = this.store.indexOf(record);
20260         var n = this.nodes[index];
20261         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20262         n.parentNode.removeChild(n);
20263         this.updateIndexes(index, index);
20264     },
20265
20266     
20267     
20268 // --------- FIXME     
20269     onAdd : function(ds, records, index)
20270     {
20271         //Roo.log(['on Add', ds, records, index] );        
20272         this.clearSelections();
20273         if(this.nodes.length == 0){
20274             this.refresh();
20275             return;
20276         }
20277         var n = this.nodes[index];
20278         for(var i = 0, len = records.length; i < len; i++){
20279             var d = this.prepareData(records[i].data, i, records[i]);
20280             if(n){
20281                 this.tpl.insertBefore(n, d);
20282             }else{
20283                 
20284                 this.tpl.append(this.el, d);
20285             }
20286         }
20287         this.updateIndexes(index);
20288     },
20289
20290     onRemove : function(ds, record, index){
20291        // Roo.log('onRemove');
20292         this.clearSelections();
20293         var el = this.dataName  ?
20294             this.el.child('.roo-tpl-' + this.dataName) :
20295             this.el; 
20296         
20297         el.dom.removeChild(this.nodes[index]);
20298         this.updateIndexes(index);
20299     },
20300
20301     /**
20302      * Refresh an individual node.
20303      * @param {Number} index
20304      */
20305     refreshNode : function(index){
20306         this.onUpdate(this.store, this.store.getAt(index));
20307     },
20308
20309     updateIndexes : function(startIndex, endIndex){
20310         var ns = this.nodes;
20311         startIndex = startIndex || 0;
20312         endIndex = endIndex || ns.length - 1;
20313         for(var i = startIndex; i <= endIndex; i++){
20314             ns[i].nodeIndex = i;
20315         }
20316     },
20317
20318     /**
20319      * Changes the data store this view uses and refresh the view.
20320      * @param {Store} store
20321      */
20322     setStore : function(store, initial){
20323         if(!initial && this.store){
20324             this.store.un("datachanged", this.refresh);
20325             this.store.un("add", this.onAdd);
20326             this.store.un("remove", this.onRemove);
20327             this.store.un("update", this.onUpdate);
20328             this.store.un("clear", this.refresh);
20329             this.store.un("beforeload", this.onBeforeLoad);
20330             this.store.un("load", this.onLoad);
20331             this.store.un("loadexception", this.onLoad);
20332         }
20333         if(store){
20334           
20335             store.on("datachanged", this.refresh, this);
20336             store.on("add", this.onAdd, this);
20337             store.on("remove", this.onRemove, this);
20338             store.on("update", this.onUpdate, this);
20339             store.on("clear", this.refresh, this);
20340             store.on("beforeload", this.onBeforeLoad, this);
20341             store.on("load", this.onLoad, this);
20342             store.on("loadexception", this.onLoad, this);
20343         }
20344         
20345         if(store){
20346             this.refresh();
20347         }
20348     },
20349     /**
20350      * onbeforeLoad - masks the loading area.
20351      *
20352      */
20353     onBeforeLoad : function(store,opts)
20354     {
20355          //Roo.log('onBeforeLoad');   
20356         if (!opts.add) {
20357             this.el.update("");
20358         }
20359         this.el.mask(this.mask ? this.mask : "Loading" ); 
20360     },
20361     onLoad : function ()
20362     {
20363         this.el.unmask();
20364     },
20365     
20366
20367     /**
20368      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20369      * @param {HTMLElement} node
20370      * @return {HTMLElement} The template node
20371      */
20372     findItemFromChild : function(node){
20373         var el = this.dataName  ?
20374             this.el.child('.roo-tpl-' + this.dataName,true) :
20375             this.el.dom; 
20376         
20377         if(!node || node.parentNode == el){
20378                     return node;
20379             }
20380             var p = node.parentNode;
20381             while(p && p != el){
20382             if(p.parentNode == el){
20383                 return p;
20384             }
20385             p = p.parentNode;
20386         }
20387             return null;
20388     },
20389
20390     /** @ignore */
20391     onClick : function(e){
20392         var item = this.findItemFromChild(e.getTarget());
20393         if(item){
20394             var index = this.indexOf(item);
20395             if(this.onItemClick(item, index, e) !== false){
20396                 this.fireEvent("click", this, index, item, e);
20397             }
20398         }else{
20399             this.clearSelections();
20400         }
20401     },
20402
20403     /** @ignore */
20404     onContextMenu : function(e){
20405         var item = this.findItemFromChild(e.getTarget());
20406         if(item){
20407             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20408         }
20409     },
20410
20411     /** @ignore */
20412     onDblClick : function(e){
20413         var item = this.findItemFromChild(e.getTarget());
20414         if(item){
20415             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20416         }
20417     },
20418
20419     onItemClick : function(item, index, e)
20420     {
20421         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20422             return false;
20423         }
20424         if (this.toggleSelect) {
20425             var m = this.isSelected(item) ? 'unselect' : 'select';
20426             //Roo.log(m);
20427             var _t = this;
20428             _t[m](item, true, false);
20429             return true;
20430         }
20431         if(this.multiSelect || this.singleSelect){
20432             if(this.multiSelect && e.shiftKey && this.lastSelection){
20433                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20434             }else{
20435                 this.select(item, this.multiSelect && e.ctrlKey);
20436                 this.lastSelection = item;
20437             }
20438             
20439             if(!this.tickable){
20440                 e.preventDefault();
20441             }
20442             
20443         }
20444         return true;
20445     },
20446
20447     /**
20448      * Get the number of selected nodes.
20449      * @return {Number}
20450      */
20451     getSelectionCount : function(){
20452         return this.selections.length;
20453     },
20454
20455     /**
20456      * Get the currently selected nodes.
20457      * @return {Array} An array of HTMLElements
20458      */
20459     getSelectedNodes : function(){
20460         return this.selections;
20461     },
20462
20463     /**
20464      * Get the indexes of the selected nodes.
20465      * @return {Array}
20466      */
20467     getSelectedIndexes : function(){
20468         var indexes = [], s = this.selections;
20469         for(var i = 0, len = s.length; i < len; i++){
20470             indexes.push(s[i].nodeIndex);
20471         }
20472         return indexes;
20473     },
20474
20475     /**
20476      * Clear all selections
20477      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20478      */
20479     clearSelections : function(suppressEvent){
20480         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20481             this.cmp.elements = this.selections;
20482             this.cmp.removeClass(this.selectedClass);
20483             this.selections = [];
20484             if(!suppressEvent){
20485                 this.fireEvent("selectionchange", this, this.selections);
20486             }
20487         }
20488     },
20489
20490     /**
20491      * Returns true if the passed node is selected
20492      * @param {HTMLElement/Number} node The node or node index
20493      * @return {Boolean}
20494      */
20495     isSelected : function(node){
20496         var s = this.selections;
20497         if(s.length < 1){
20498             return false;
20499         }
20500         node = this.getNode(node);
20501         return s.indexOf(node) !== -1;
20502     },
20503
20504     /**
20505      * Selects nodes.
20506      * @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
20507      * @param {Boolean} keepExisting (optional) true to keep existing selections
20508      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20509      */
20510     select : function(nodeInfo, keepExisting, suppressEvent){
20511         if(nodeInfo instanceof Array){
20512             if(!keepExisting){
20513                 this.clearSelections(true);
20514             }
20515             for(var i = 0, len = nodeInfo.length; i < len; i++){
20516                 this.select(nodeInfo[i], true, true);
20517             }
20518             return;
20519         } 
20520         var node = this.getNode(nodeInfo);
20521         if(!node || this.isSelected(node)){
20522             return; // already selected.
20523         }
20524         if(!keepExisting){
20525             this.clearSelections(true);
20526         }
20527         
20528         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20529             Roo.fly(node).addClass(this.selectedClass);
20530             this.selections.push(node);
20531             if(!suppressEvent){
20532                 this.fireEvent("selectionchange", this, this.selections);
20533             }
20534         }
20535         
20536         
20537     },
20538       /**
20539      * Unselects nodes.
20540      * @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
20541      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20543      */
20544     unselect : function(nodeInfo, keepExisting, suppressEvent)
20545     {
20546         if(nodeInfo instanceof Array){
20547             Roo.each(this.selections, function(s) {
20548                 this.unselect(s, nodeInfo);
20549             }, this);
20550             return;
20551         }
20552         var node = this.getNode(nodeInfo);
20553         if(!node || !this.isSelected(node)){
20554             //Roo.log("not selected");
20555             return; // not selected.
20556         }
20557         // fireevent???
20558         var ns = [];
20559         Roo.each(this.selections, function(s) {
20560             if (s == node ) {
20561                 Roo.fly(node).removeClass(this.selectedClass);
20562
20563                 return;
20564             }
20565             ns.push(s);
20566         },this);
20567         
20568         this.selections= ns;
20569         this.fireEvent("selectionchange", this, this.selections);
20570     },
20571
20572     /**
20573      * Gets a template node.
20574      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20575      * @return {HTMLElement} The node or null if it wasn't found
20576      */
20577     getNode : function(nodeInfo){
20578         if(typeof nodeInfo == "string"){
20579             return document.getElementById(nodeInfo);
20580         }else if(typeof nodeInfo == "number"){
20581             return this.nodes[nodeInfo];
20582         }
20583         return nodeInfo;
20584     },
20585
20586     /**
20587      * Gets a range template nodes.
20588      * @param {Number} startIndex
20589      * @param {Number} endIndex
20590      * @return {Array} An array of nodes
20591      */
20592     getNodes : function(start, end){
20593         var ns = this.nodes;
20594         start = start || 0;
20595         end = typeof end == "undefined" ? ns.length - 1 : end;
20596         var nodes = [];
20597         if(start <= end){
20598             for(var i = start; i <= end; i++){
20599                 nodes.push(ns[i]);
20600             }
20601         } else{
20602             for(var i = start; i >= end; i--){
20603                 nodes.push(ns[i]);
20604             }
20605         }
20606         return nodes;
20607     },
20608
20609     /**
20610      * Finds the index of the passed node
20611      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20612      * @return {Number} The index of the node or -1
20613      */
20614     indexOf : function(node){
20615         node = this.getNode(node);
20616         if(typeof node.nodeIndex == "number"){
20617             return node.nodeIndex;
20618         }
20619         var ns = this.nodes;
20620         for(var i = 0, len = ns.length; i < len; i++){
20621             if(ns[i] == node){
20622                 return i;
20623             }
20624         }
20625         return -1;
20626     }
20627 });
20628 /*
20629  * - LGPL
20630  *
20631  * based on jquery fullcalendar
20632  * 
20633  */
20634
20635 Roo.bootstrap = Roo.bootstrap || {};
20636 /**
20637  * @class Roo.bootstrap.Calendar
20638  * @extends Roo.bootstrap.Component
20639  * Bootstrap Calendar class
20640  * @cfg {Boolean} loadMask (true|false) default false
20641  * @cfg {Object} header generate the user specific header of the calendar, default false
20642
20643  * @constructor
20644  * Create a new Container
20645  * @param {Object} config The config object
20646  */
20647
20648
20649
20650 Roo.bootstrap.Calendar = function(config){
20651     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20652      this.addEvents({
20653         /**
20654              * @event select
20655              * Fires when a date is selected
20656              * @param {DatePicker} this
20657              * @param {Date} date The selected date
20658              */
20659         'select': true,
20660         /**
20661              * @event monthchange
20662              * Fires when the displayed month changes 
20663              * @param {DatePicker} this
20664              * @param {Date} date The selected month
20665              */
20666         'monthchange': true,
20667         /**
20668              * @event evententer
20669              * Fires when mouse over an event
20670              * @param {Calendar} this
20671              * @param {event} Event
20672              */
20673         'evententer': true,
20674         /**
20675              * @event eventleave
20676              * Fires when the mouse leaves an
20677              * @param {Calendar} this
20678              * @param {event}
20679              */
20680         'eventleave': true,
20681         /**
20682              * @event eventclick
20683              * Fires when the mouse click an
20684              * @param {Calendar} this
20685              * @param {event}
20686              */
20687         'eventclick': true
20688         
20689     });
20690
20691 };
20692
20693 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20694     
20695           /**
20696      * @cfg {Roo.data.Store} store
20697      * The data source for the calendar
20698      */
20699         store : false,
20700      /**
20701      * @cfg {Number} startDay
20702      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20703      */
20704     startDay : 0,
20705     
20706     loadMask : false,
20707     
20708     header : false,
20709       
20710     getAutoCreate : function(){
20711         
20712         
20713         var fc_button = function(name, corner, style, content ) {
20714             return Roo.apply({},{
20715                 tag : 'span',
20716                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20717                          (corner.length ?
20718                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20719                             ''
20720                         ),
20721                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20722                 unselectable: 'on'
20723             });
20724         };
20725         
20726         var header = {};
20727         
20728         if(!this.header){
20729             header = {
20730                 tag : 'table',
20731                 cls : 'fc-header',
20732                 style : 'width:100%',
20733                 cn : [
20734                     {
20735                         tag: 'tr',
20736                         cn : [
20737                             {
20738                                 tag : 'td',
20739                                 cls : 'fc-header-left',
20740                                 cn : [
20741                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20742                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20743                                     { tag: 'span', cls: 'fc-header-space' },
20744                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20745
20746
20747                                 ]
20748                             },
20749
20750                             {
20751                                 tag : 'td',
20752                                 cls : 'fc-header-center',
20753                                 cn : [
20754                                     {
20755                                         tag: 'span',
20756                                         cls: 'fc-header-title',
20757                                         cn : {
20758                                             tag: 'H2',
20759                                             html : 'month / year'
20760                                         }
20761                                     }
20762
20763                                 ]
20764                             },
20765                             {
20766                                 tag : 'td',
20767                                 cls : 'fc-header-right',
20768                                 cn : [
20769                               /*      fc_button('month', 'left', '', 'month' ),
20770                                     fc_button('week', '', '', 'week' ),
20771                                     fc_button('day', 'right', '', 'day' )
20772                                 */    
20773
20774                                 ]
20775                             }
20776
20777                         ]
20778                     }
20779                 ]
20780             };
20781         }
20782         
20783         header = this.header;
20784         
20785        
20786         var cal_heads = function() {
20787             var ret = [];
20788             // fixme - handle this.
20789             
20790             for (var i =0; i < Date.dayNames.length; i++) {
20791                 var d = Date.dayNames[i];
20792                 ret.push({
20793                     tag: 'th',
20794                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20795                     html : d.substring(0,3)
20796                 });
20797                 
20798             }
20799             ret[0].cls += ' fc-first';
20800             ret[6].cls += ' fc-last';
20801             return ret;
20802         };
20803         var cal_cell = function(n) {
20804             return  {
20805                 tag: 'td',
20806                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20807                 cn : [
20808                     {
20809                         cn : [
20810                             {
20811                                 cls: 'fc-day-number',
20812                                 html: 'D'
20813                             },
20814                             {
20815                                 cls: 'fc-day-content',
20816                              
20817                                 cn : [
20818                                      {
20819                                         style: 'position: relative;' // height: 17px;
20820                                     }
20821                                 ]
20822                             }
20823                             
20824                             
20825                         ]
20826                     }
20827                 ]
20828                 
20829             }
20830         };
20831         var cal_rows = function() {
20832             
20833             var ret = [];
20834             for (var r = 0; r < 6; r++) {
20835                 var row= {
20836                     tag : 'tr',
20837                     cls : 'fc-week',
20838                     cn : []
20839                 };
20840                 
20841                 for (var i =0; i < Date.dayNames.length; i++) {
20842                     var d = Date.dayNames[i];
20843                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20844
20845                 }
20846                 row.cn[0].cls+=' fc-first';
20847                 row.cn[0].cn[0].style = 'min-height:90px';
20848                 row.cn[6].cls+=' fc-last';
20849                 ret.push(row);
20850                 
20851             }
20852             ret[0].cls += ' fc-first';
20853             ret[4].cls += ' fc-prev-last';
20854             ret[5].cls += ' fc-last';
20855             return ret;
20856             
20857         };
20858         
20859         var cal_table = {
20860             tag: 'table',
20861             cls: 'fc-border-separate',
20862             style : 'width:100%',
20863             cellspacing  : 0,
20864             cn : [
20865                 { 
20866                     tag: 'thead',
20867                     cn : [
20868                         { 
20869                             tag: 'tr',
20870                             cls : 'fc-first fc-last',
20871                             cn : cal_heads()
20872                         }
20873                     ]
20874                 },
20875                 { 
20876                     tag: 'tbody',
20877                     cn : cal_rows()
20878                 }
20879                   
20880             ]
20881         };
20882          
20883          var cfg = {
20884             cls : 'fc fc-ltr',
20885             cn : [
20886                 header,
20887                 {
20888                     cls : 'fc-content',
20889                     style : "position: relative;",
20890                     cn : [
20891                         {
20892                             cls : 'fc-view fc-view-month fc-grid',
20893                             style : 'position: relative',
20894                             unselectable : 'on',
20895                             cn : [
20896                                 {
20897                                     cls : 'fc-event-container',
20898                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20899                                 },
20900                                 cal_table
20901                             ]
20902                         }
20903                     ]
20904     
20905                 }
20906            ] 
20907             
20908         };
20909         
20910          
20911         
20912         return cfg;
20913     },
20914     
20915     
20916     initEvents : function()
20917     {
20918         if(!this.store){
20919             throw "can not find store for calendar";
20920         }
20921         
20922         var mark = {
20923             tag: "div",
20924             cls:"x-dlg-mask",
20925             style: "text-align:center",
20926             cn: [
20927                 {
20928                     tag: "div",
20929                     style: "background-color:white;width:50%;margin:250 auto",
20930                     cn: [
20931                         {
20932                             tag: "img",
20933                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20934                         },
20935                         {
20936                             tag: "span",
20937                             html: "Loading"
20938                         }
20939                         
20940                     ]
20941                 }
20942             ]
20943         };
20944         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20945         
20946         var size = this.el.select('.fc-content', true).first().getSize();
20947         this.maskEl.setSize(size.width, size.height);
20948         this.maskEl.enableDisplayMode("block");
20949         if(!this.loadMask){
20950             this.maskEl.hide();
20951         }
20952         
20953         this.store = Roo.factory(this.store, Roo.data);
20954         this.store.on('load', this.onLoad, this);
20955         this.store.on('beforeload', this.onBeforeLoad, this);
20956         
20957         this.resize();
20958         
20959         this.cells = this.el.select('.fc-day',true);
20960         //Roo.log(this.cells);
20961         this.textNodes = this.el.query('.fc-day-number');
20962         this.cells.addClassOnOver('fc-state-hover');
20963         
20964         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20965         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20966         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20967         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20968         
20969         this.on('monthchange', this.onMonthChange, this);
20970         
20971         this.update(new Date().clearTime());
20972     },
20973     
20974     resize : function() {
20975         var sz  = this.el.getSize();
20976         
20977         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20978         this.el.select('.fc-day-content div',true).setHeight(34);
20979     },
20980     
20981     
20982     // private
20983     showPrevMonth : function(e){
20984         this.update(this.activeDate.add("mo", -1));
20985     },
20986     showToday : function(e){
20987         this.update(new Date().clearTime());
20988     },
20989     // private
20990     showNextMonth : function(e){
20991         this.update(this.activeDate.add("mo", 1));
20992     },
20993
20994     // private
20995     showPrevYear : function(){
20996         this.update(this.activeDate.add("y", -1));
20997     },
20998
20999     // private
21000     showNextYear : function(){
21001         this.update(this.activeDate.add("y", 1));
21002     },
21003
21004     
21005    // private
21006     update : function(date)
21007     {
21008         var vd = this.activeDate;
21009         this.activeDate = date;
21010 //        if(vd && this.el){
21011 //            var t = date.getTime();
21012 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21013 //                Roo.log('using add remove');
21014 //                
21015 //                this.fireEvent('monthchange', this, date);
21016 //                
21017 //                this.cells.removeClass("fc-state-highlight");
21018 //                this.cells.each(function(c){
21019 //                   if(c.dateValue == t){
21020 //                       c.addClass("fc-state-highlight");
21021 //                       setTimeout(function(){
21022 //                            try{c.dom.firstChild.focus();}catch(e){}
21023 //                       }, 50);
21024 //                       return false;
21025 //                   }
21026 //                   return true;
21027 //                });
21028 //                return;
21029 //            }
21030 //        }
21031         
21032         var days = date.getDaysInMonth();
21033         
21034         var firstOfMonth = date.getFirstDateOfMonth();
21035         var startingPos = firstOfMonth.getDay()-this.startDay;
21036         
21037         if(startingPos < this.startDay){
21038             startingPos += 7;
21039         }
21040         
21041         var pm = date.add(Date.MONTH, -1);
21042         var prevStart = pm.getDaysInMonth()-startingPos;
21043 //        
21044         this.cells = this.el.select('.fc-day',true);
21045         this.textNodes = this.el.query('.fc-day-number');
21046         this.cells.addClassOnOver('fc-state-hover');
21047         
21048         var cells = this.cells.elements;
21049         var textEls = this.textNodes;
21050         
21051         Roo.each(cells, function(cell){
21052             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21053         });
21054         
21055         days += startingPos;
21056
21057         // convert everything to numbers so it's fast
21058         var day = 86400000;
21059         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21060         //Roo.log(d);
21061         //Roo.log(pm);
21062         //Roo.log(prevStart);
21063         
21064         var today = new Date().clearTime().getTime();
21065         var sel = date.clearTime().getTime();
21066         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21067         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21068         var ddMatch = this.disabledDatesRE;
21069         var ddText = this.disabledDatesText;
21070         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21071         var ddaysText = this.disabledDaysText;
21072         var format = this.format;
21073         
21074         var setCellClass = function(cal, cell){
21075             cell.row = 0;
21076             cell.events = [];
21077             cell.more = [];
21078             //Roo.log('set Cell Class');
21079             cell.title = "";
21080             var t = d.getTime();
21081             
21082             //Roo.log(d);
21083             
21084             cell.dateValue = t;
21085             if(t == today){
21086                 cell.className += " fc-today";
21087                 cell.className += " fc-state-highlight";
21088                 cell.title = cal.todayText;
21089             }
21090             if(t == sel){
21091                 // disable highlight in other month..
21092                 //cell.className += " fc-state-highlight";
21093                 
21094             }
21095             // disabling
21096             if(t < min) {
21097                 cell.className = " fc-state-disabled";
21098                 cell.title = cal.minText;
21099                 return;
21100             }
21101             if(t > max) {
21102                 cell.className = " fc-state-disabled";
21103                 cell.title = cal.maxText;
21104                 return;
21105             }
21106             if(ddays){
21107                 if(ddays.indexOf(d.getDay()) != -1){
21108                     cell.title = ddaysText;
21109                     cell.className = " fc-state-disabled";
21110                 }
21111             }
21112             if(ddMatch && format){
21113                 var fvalue = d.dateFormat(format);
21114                 if(ddMatch.test(fvalue)){
21115                     cell.title = ddText.replace("%0", fvalue);
21116                     cell.className = " fc-state-disabled";
21117                 }
21118             }
21119             
21120             if (!cell.initialClassName) {
21121                 cell.initialClassName = cell.dom.className;
21122             }
21123             
21124             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21125         };
21126
21127         var i = 0;
21128         
21129         for(; i < startingPos; i++) {
21130             textEls[i].innerHTML = (++prevStart);
21131             d.setDate(d.getDate()+1);
21132             
21133             cells[i].className = "fc-past fc-other-month";
21134             setCellClass(this, cells[i]);
21135         }
21136         
21137         var intDay = 0;
21138         
21139         for(; i < days; i++){
21140             intDay = i - startingPos + 1;
21141             textEls[i].innerHTML = (intDay);
21142             d.setDate(d.getDate()+1);
21143             
21144             cells[i].className = ''; // "x-date-active";
21145             setCellClass(this, cells[i]);
21146         }
21147         var extraDays = 0;
21148         
21149         for(; i < 42; i++) {
21150             textEls[i].innerHTML = (++extraDays);
21151             d.setDate(d.getDate()+1);
21152             
21153             cells[i].className = "fc-future fc-other-month";
21154             setCellClass(this, cells[i]);
21155         }
21156         
21157         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21158         
21159         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21160         
21161         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21162         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21163         
21164         if(totalRows != 6){
21165             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21166             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21167         }
21168         
21169         this.fireEvent('monthchange', this, date);
21170         
21171         
21172         /*
21173         if(!this.internalRender){
21174             var main = this.el.dom.firstChild;
21175             var w = main.offsetWidth;
21176             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21177             Roo.fly(main).setWidth(w);
21178             this.internalRender = true;
21179             // opera does not respect the auto grow header center column
21180             // then, after it gets a width opera refuses to recalculate
21181             // without a second pass
21182             if(Roo.isOpera && !this.secondPass){
21183                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21184                 this.secondPass = true;
21185                 this.update.defer(10, this, [date]);
21186             }
21187         }
21188         */
21189         
21190     },
21191     
21192     findCell : function(dt) {
21193         dt = dt.clearTime().getTime();
21194         var ret = false;
21195         this.cells.each(function(c){
21196             //Roo.log("check " +c.dateValue + '?=' + dt);
21197             if(c.dateValue == dt){
21198                 ret = c;
21199                 return false;
21200             }
21201             return true;
21202         });
21203         
21204         return ret;
21205     },
21206     
21207     findCells : function(ev) {
21208         var s = ev.start.clone().clearTime().getTime();
21209        // Roo.log(s);
21210         var e= ev.end.clone().clearTime().getTime();
21211        // Roo.log(e);
21212         var ret = [];
21213         this.cells.each(function(c){
21214              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21215             
21216             if(c.dateValue > e){
21217                 return ;
21218             }
21219             if(c.dateValue < s){
21220                 return ;
21221             }
21222             ret.push(c);
21223         });
21224         
21225         return ret;    
21226     },
21227     
21228 //    findBestRow: function(cells)
21229 //    {
21230 //        var ret = 0;
21231 //        
21232 //        for (var i =0 ; i < cells.length;i++) {
21233 //            ret  = Math.max(cells[i].rows || 0,ret);
21234 //        }
21235 //        return ret;
21236 //        
21237 //    },
21238     
21239     
21240     addItem : function(ev)
21241     {
21242         // look for vertical location slot in
21243         var cells = this.findCells(ev);
21244         
21245 //        ev.row = this.findBestRow(cells);
21246         
21247         // work out the location.
21248         
21249         var crow = false;
21250         var rows = [];
21251         for(var i =0; i < cells.length; i++) {
21252             
21253             cells[i].row = cells[0].row;
21254             
21255             if(i == 0){
21256                 cells[i].row = cells[i].row + 1;
21257             }
21258             
21259             if (!crow) {
21260                 crow = {
21261                     start : cells[i],
21262                     end :  cells[i]
21263                 };
21264                 continue;
21265             }
21266             if (crow.start.getY() == cells[i].getY()) {
21267                 // on same row.
21268                 crow.end = cells[i];
21269                 continue;
21270             }
21271             // different row.
21272             rows.push(crow);
21273             crow = {
21274                 start: cells[i],
21275                 end : cells[i]
21276             };
21277             
21278         }
21279         
21280         rows.push(crow);
21281         ev.els = [];
21282         ev.rows = rows;
21283         ev.cells = cells;
21284         
21285         cells[0].events.push(ev);
21286         
21287         this.calevents.push(ev);
21288     },
21289     
21290     clearEvents: function() {
21291         
21292         if(!this.calevents){
21293             return;
21294         }
21295         
21296         Roo.each(this.cells.elements, function(c){
21297             c.row = 0;
21298             c.events = [];
21299             c.more = [];
21300         });
21301         
21302         Roo.each(this.calevents, function(e) {
21303             Roo.each(e.els, function(el) {
21304                 el.un('mouseenter' ,this.onEventEnter, this);
21305                 el.un('mouseleave' ,this.onEventLeave, this);
21306                 el.remove();
21307             },this);
21308         },this);
21309         
21310         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21311             e.remove();
21312         });
21313         
21314     },
21315     
21316     renderEvents: function()
21317     {   
21318         var _this = this;
21319         
21320         this.cells.each(function(c) {
21321             
21322             if(c.row < 5){
21323                 return;
21324             }
21325             
21326             var ev = c.events;
21327             
21328             var r = 4;
21329             if(c.row != c.events.length){
21330                 r = 4 - (4 - (c.row - c.events.length));
21331             }
21332             
21333             c.events = ev.slice(0, r);
21334             c.more = ev.slice(r);
21335             
21336             if(c.more.length && c.more.length == 1){
21337                 c.events.push(c.more.pop());
21338             }
21339             
21340             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21341             
21342         });
21343             
21344         this.cells.each(function(c) {
21345             
21346             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21347             
21348             
21349             for (var e = 0; e < c.events.length; e++){
21350                 var ev = c.events[e];
21351                 var rows = ev.rows;
21352                 
21353                 for(var i = 0; i < rows.length; i++) {
21354                 
21355                     // how many rows should it span..
21356
21357                     var  cfg = {
21358                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21359                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21360
21361                         unselectable : "on",
21362                         cn : [
21363                             {
21364                                 cls: 'fc-event-inner',
21365                                 cn : [
21366     //                                {
21367     //                                  tag:'span',
21368     //                                  cls: 'fc-event-time',
21369     //                                  html : cells.length > 1 ? '' : ev.time
21370     //                                },
21371                                     {
21372                                       tag:'span',
21373                                       cls: 'fc-event-title',
21374                                       html : String.format('{0}', ev.title)
21375                                     }
21376
21377
21378                                 ]
21379                             },
21380                             {
21381                                 cls: 'ui-resizable-handle ui-resizable-e',
21382                                 html : '&nbsp;&nbsp;&nbsp'
21383                             }
21384
21385                         ]
21386                     };
21387
21388                     if (i == 0) {
21389                         cfg.cls += ' fc-event-start';
21390                     }
21391                     if ((i+1) == rows.length) {
21392                         cfg.cls += ' fc-event-end';
21393                     }
21394
21395                     var ctr = _this.el.select('.fc-event-container',true).first();
21396                     var cg = ctr.createChild(cfg);
21397
21398                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21399                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21400
21401                     var r = (c.more.length) ? 1 : 0;
21402                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21403                     cg.setWidth(ebox.right - sbox.x -2);
21404
21405                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21406                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21407                     cg.on('click', _this.onEventClick, _this, ev);
21408
21409                     ev.els.push(cg);
21410                     
21411                 }
21412                 
21413             }
21414             
21415             
21416             if(c.more.length){
21417                 var  cfg = {
21418                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21419                     style : 'position: absolute',
21420                     unselectable : "on",
21421                     cn : [
21422                         {
21423                             cls: 'fc-event-inner',
21424                             cn : [
21425                                 {
21426                                   tag:'span',
21427                                   cls: 'fc-event-title',
21428                                   html : 'More'
21429                                 }
21430
21431
21432                             ]
21433                         },
21434                         {
21435                             cls: 'ui-resizable-handle ui-resizable-e',
21436                             html : '&nbsp;&nbsp;&nbsp'
21437                         }
21438
21439                     ]
21440                 };
21441
21442                 var ctr = _this.el.select('.fc-event-container',true).first();
21443                 var cg = ctr.createChild(cfg);
21444
21445                 var sbox = c.select('.fc-day-content',true).first().getBox();
21446                 var ebox = c.select('.fc-day-content',true).first().getBox();
21447                 //Roo.log(cg);
21448                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21449                 cg.setWidth(ebox.right - sbox.x -2);
21450
21451                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21452                 
21453             }
21454             
21455         });
21456         
21457         
21458         
21459     },
21460     
21461     onEventEnter: function (e, el,event,d) {
21462         this.fireEvent('evententer', this, el, event);
21463     },
21464     
21465     onEventLeave: function (e, el,event,d) {
21466         this.fireEvent('eventleave', this, el, event);
21467     },
21468     
21469     onEventClick: function (e, el,event,d) {
21470         this.fireEvent('eventclick', this, el, event);
21471     },
21472     
21473     onMonthChange: function () {
21474         this.store.load();
21475     },
21476     
21477     onMoreEventClick: function(e, el, more)
21478     {
21479         var _this = this;
21480         
21481         this.calpopover.placement = 'right';
21482         this.calpopover.setTitle('More');
21483         
21484         this.calpopover.setContent('');
21485         
21486         var ctr = this.calpopover.el.select('.popover-content', true).first();
21487         
21488         Roo.each(more, function(m){
21489             var cfg = {
21490                 cls : 'fc-event-hori fc-event-draggable',
21491                 html : m.title
21492             };
21493             var cg = ctr.createChild(cfg);
21494             
21495             cg.on('click', _this.onEventClick, _this, m);
21496         });
21497         
21498         this.calpopover.show(el);
21499         
21500         
21501     },
21502     
21503     onLoad: function () 
21504     {   
21505         this.calevents = [];
21506         var cal = this;
21507         
21508         if(this.store.getCount() > 0){
21509             this.store.data.each(function(d){
21510                cal.addItem({
21511                     id : d.data.id,
21512                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21513                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21514                     time : d.data.start_time,
21515                     title : d.data.title,
21516                     description : d.data.description,
21517                     venue : d.data.venue
21518                 });
21519             });
21520         }
21521         
21522         this.renderEvents();
21523         
21524         if(this.calevents.length && this.loadMask){
21525             this.maskEl.hide();
21526         }
21527     },
21528     
21529     onBeforeLoad: function()
21530     {
21531         this.clearEvents();
21532         if(this.loadMask){
21533             this.maskEl.show();
21534         }
21535     }
21536 });
21537
21538  
21539  /*
21540  * - LGPL
21541  *
21542  * element
21543  * 
21544  */
21545
21546 /**
21547  * @class Roo.bootstrap.Popover
21548  * @extends Roo.bootstrap.Component
21549  * @parent none builder
21550  * @children Roo.bootstrap.Component
21551  * Bootstrap Popover class
21552  * @cfg {String} html contents of the popover   (or false to use children..)
21553  * @cfg {String} title of popover (or false to hide)
21554  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21555  * @cfg {String} trigger click || hover (or false to trigger manually)
21556  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21557  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21558  *      - if false and it has a 'parent' then it will be automatically added to that element
21559  *      - if string - Roo.get  will be called 
21560  * @cfg {Number} delay - delay before showing
21561  
21562  * @constructor
21563  * Create a new Popover
21564  * @param {Object} config The config object
21565  */
21566
21567 Roo.bootstrap.Popover = function(config){
21568     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21569     
21570     this.addEvents({
21571         // raw events
21572          /**
21573          * @event show
21574          * After the popover show
21575          * 
21576          * @param {Roo.bootstrap.Popover} this
21577          */
21578         "show" : true,
21579         /**
21580          * @event hide
21581          * After the popover hide
21582          * 
21583          * @param {Roo.bootstrap.Popover} this
21584          */
21585         "hide" : true
21586     });
21587 };
21588
21589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21590     
21591     title: false,
21592     html: false,
21593     
21594     placement : 'right',
21595     trigger : 'hover', // hover
21596     modal : false,
21597     delay : 0,
21598     
21599     over: false,
21600     
21601     can_build_overlaid : false,
21602     
21603     maskEl : false, // the mask element
21604     headerEl : false,
21605     contentEl : false,
21606     alignEl : false, // when show is called with an element - this get's stored.
21607     
21608     getChildContainer : function()
21609     {
21610         return this.contentEl;
21611         
21612     },
21613     getPopoverHeader : function()
21614     {
21615         this.title = true; // flag not to hide it..
21616         this.headerEl.addClass('p-0');
21617         return this.headerEl
21618     },
21619     
21620     
21621     getAutoCreate : function(){
21622          
21623         var cfg = {
21624            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21625            style: 'display:block',
21626            cn : [
21627                 {
21628                     cls : 'arrow'
21629                 },
21630                 {
21631                     cls : 'popover-inner ',
21632                     cn : [
21633                         {
21634                             tag: 'h3',
21635                             cls: 'popover-title popover-header',
21636                             html : this.title === false ? '' : this.title
21637                         },
21638                         {
21639                             cls : 'popover-content popover-body '  + (this.cls || ''),
21640                             html : this.html || ''
21641                         }
21642                     ]
21643                     
21644                 }
21645            ]
21646         };
21647         
21648         return cfg;
21649     },
21650     /**
21651      * @param {string} the title
21652      */
21653     setTitle: function(str)
21654     {
21655         this.title = str;
21656         if (this.el) {
21657             this.headerEl.dom.innerHTML = str;
21658         }
21659         
21660     },
21661     /**
21662      * @param {string} the body content
21663      */
21664     setContent: function(str)
21665     {
21666         this.html = str;
21667         if (this.contentEl) {
21668             this.contentEl.dom.innerHTML = str;
21669         }
21670         
21671     },
21672     // as it get's added to the bottom of the page.
21673     onRender : function(ct, position)
21674     {
21675         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21676         
21677         
21678         
21679         if(!this.el){
21680             var cfg = Roo.apply({},  this.getAutoCreate());
21681             cfg.id = Roo.id();
21682             
21683             if (this.cls) {
21684                 cfg.cls += ' ' + this.cls;
21685             }
21686             if (this.style) {
21687                 cfg.style = this.style;
21688             }
21689             //Roo.log("adding to ");
21690             this.el = Roo.get(document.body).createChild(cfg, position);
21691 //            Roo.log(this.el);
21692         }
21693         
21694         this.contentEl = this.el.select('.popover-content',true).first();
21695         this.headerEl =  this.el.select('.popover-title',true).first();
21696         
21697         var nitems = [];
21698         if(typeof(this.items) != 'undefined'){
21699             var items = this.items;
21700             delete this.items;
21701
21702             for(var i =0;i < items.length;i++) {
21703                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21704             }
21705         }
21706
21707         this.items = nitems;
21708         
21709         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21710         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21711         
21712         
21713         
21714         this.initEvents();
21715     },
21716     
21717     resizeMask : function()
21718     {
21719         this.maskEl.setSize(
21720             Roo.lib.Dom.getViewWidth(true),
21721             Roo.lib.Dom.getViewHeight(true)
21722         );
21723     },
21724     
21725     initEvents : function()
21726     {
21727         
21728         if (!this.modal) { 
21729             Roo.bootstrap.Popover.register(this);
21730         }
21731          
21732         this.arrowEl = this.el.select('.arrow',true).first();
21733         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21734         this.el.enableDisplayMode('block');
21735         this.el.hide();
21736  
21737         
21738         if (this.over === false && !this.parent()) {
21739             return; 
21740         }
21741         if (this.triggers === false) {
21742             return;
21743         }
21744          
21745         // support parent
21746         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21747         var triggers = this.trigger ? this.trigger.split(' ') : [];
21748         Roo.each(triggers, function(trigger) {
21749         
21750             if (trigger == 'click') {
21751                 on_el.on('click', this.toggle, this);
21752             } else if (trigger != 'manual') {
21753                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21754                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21755       
21756                 on_el.on(eventIn  ,this.enter, this);
21757                 on_el.on(eventOut, this.leave, this);
21758             }
21759         }, this);
21760     },
21761     
21762     
21763     // private
21764     timeout : null,
21765     hoverState : null,
21766     
21767     toggle : function () {
21768         this.hoverState == 'in' ? this.leave() : this.enter();
21769     },
21770     
21771     enter : function () {
21772         
21773         clearTimeout(this.timeout);
21774     
21775         this.hoverState = 'in';
21776     
21777         if (!this.delay || !this.delay.show) {
21778             this.show();
21779             return;
21780         }
21781         var _t = this;
21782         this.timeout = setTimeout(function () {
21783             if (_t.hoverState == 'in') {
21784                 _t.show();
21785             }
21786         }, this.delay.show)
21787     },
21788     
21789     leave : function() {
21790         clearTimeout(this.timeout);
21791     
21792         this.hoverState = 'out';
21793     
21794         if (!this.delay || !this.delay.hide) {
21795             this.hide();
21796             return;
21797         }
21798         var _t = this;
21799         this.timeout = setTimeout(function () {
21800             if (_t.hoverState == 'out') {
21801                 _t.hide();
21802             }
21803         }, this.delay.hide)
21804     },
21805     
21806     /**
21807      * update the position of the dialog
21808      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21809      * 
21810      *
21811      */
21812     
21813     doAlign : function()
21814     {
21815         
21816         if (this.alignEl) {
21817             this.updatePosition(this.placement, true);
21818              
21819         } else {
21820             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21821             var es = this.el.getSize();
21822             var x = Roo.lib.Dom.getViewWidth()/2;
21823             var y = Roo.lib.Dom.getViewHeight()/2;
21824             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21825             
21826         }
21827
21828          
21829          
21830         
21831         
21832     },
21833     
21834     /**
21835      * Show the popover
21836      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21837      * @param {string} (left|right|top|bottom) position
21838      */
21839     show : function (on_el, placement)
21840     {
21841         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21842         on_el = on_el || false; // default to false
21843          
21844         if (!on_el) {
21845             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21846                 on_el = this.parent().el;
21847             } else if (this.over) {
21848                 on_el = Roo.get(this.over);
21849             }
21850             
21851         }
21852         
21853         this.alignEl = Roo.get( on_el );
21854
21855         if (!this.el) {
21856             this.render(document.body);
21857         }
21858         
21859         
21860          
21861         
21862         if (this.title === false) {
21863             this.headerEl.hide();
21864         }
21865         
21866        
21867         this.el.show();
21868         this.el.dom.style.display = 'block';
21869          
21870         this.doAlign();
21871         
21872         //var arrow = this.el.select('.arrow',true).first();
21873         //arrow.set(align[2], 
21874         
21875         this.el.addClass('in');
21876         
21877          
21878         
21879         this.hoverState = 'in';
21880         
21881         if (this.modal) {
21882             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21883             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21884             this.maskEl.dom.style.display = 'block';
21885             this.maskEl.addClass('show');
21886         }
21887         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21888  
21889         this.fireEvent('show', this);
21890         
21891     },
21892     /**
21893      * fire this manually after loading a grid in the table for example
21894      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21895      * @param {Boolean} try and move it if we cant get right position.
21896      */
21897     updatePosition : function(placement, try_move)
21898     {
21899         // allow for calling with no parameters
21900         placement = placement   ? placement :  this.placement;
21901         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21902         
21903         this.el.removeClass([
21904             'fade','top','bottom', 'left', 'right','in',
21905             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21906         ]);
21907         this.el.addClass(placement + ' bs-popover-' + placement);
21908         
21909         if (!this.alignEl ) {
21910             return false;
21911         }
21912         
21913         switch (placement) {
21914             case 'right':
21915                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21916                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21917                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21918                     //normal display... or moved up/down.
21919                     this.el.setXY(offset);
21920                     var xy = this.alignEl.getAnchorXY('tr', false);
21921                     xy[0]+=2;xy[1]+=5;
21922                     this.arrowEl.setXY(xy);
21923                     return true;
21924                 }
21925                 // continue through...
21926                 return this.updatePosition('left', false);
21927                 
21928             
21929             case 'left':
21930                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21931                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21932                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21933                     //normal display... or moved up/down.
21934                     this.el.setXY(offset);
21935                     var xy = this.alignEl.getAnchorXY('tl', false);
21936                     xy[0]-=10;xy[1]+=5; // << fix me
21937                     this.arrowEl.setXY(xy);
21938                     return true;
21939                 }
21940                 // call self...
21941                 return this.updatePosition('right', false);
21942             
21943             case 'top':
21944                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21945                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21946                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21947                     //normal display... or moved up/down.
21948                     this.el.setXY(offset);
21949                     var xy = this.alignEl.getAnchorXY('t', false);
21950                     xy[1]-=10; // << fix me
21951                     this.arrowEl.setXY(xy);
21952                     return true;
21953                 }
21954                 // fall through
21955                return this.updatePosition('bottom', false);
21956             
21957             case 'bottom':
21958                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21959                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21960                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21961                     //normal display... or moved up/down.
21962                     this.el.setXY(offset);
21963                     var xy = this.alignEl.getAnchorXY('b', false);
21964                      xy[1]+=2; // << fix me
21965                     this.arrowEl.setXY(xy);
21966                     return true;
21967                 }
21968                 // fall through
21969                 return this.updatePosition('top', false);
21970                 
21971             
21972         }
21973         
21974         
21975         return false;
21976     },
21977     
21978     hide : function()
21979     {
21980         this.el.setXY([0,0]);
21981         this.el.removeClass('in');
21982         this.el.hide();
21983         this.hoverState = null;
21984         this.maskEl.hide(); // always..
21985         this.fireEvent('hide', this);
21986     }
21987     
21988 });
21989
21990
21991 Roo.apply(Roo.bootstrap.Popover, {
21992
21993     alignment : {
21994         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21995         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21996         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21997         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21998     },
21999     
22000     zIndex : 20001,
22001
22002     clickHander : false,
22003     
22004     
22005
22006     onMouseDown : function(e)
22007     {
22008         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22009             /// what is nothing is showing..
22010             this.hideAll();
22011         }
22012          
22013     },
22014     
22015     
22016     popups : [],
22017     
22018     register : function(popup)
22019     {
22020         if (!Roo.bootstrap.Popover.clickHandler) {
22021             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22022         }
22023         // hide other popups.
22024         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22025         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22026         this.hideAll(); //<< why?
22027         //this.popups.push(popup);
22028     },
22029     hideAll : function()
22030     {
22031         this.popups.forEach(function(p) {
22032             p.hide();
22033         });
22034     },
22035     onShow : function() {
22036         Roo.bootstrap.Popover.popups.push(this);
22037     },
22038     onHide : function() {
22039         Roo.bootstrap.Popover.popups.remove(this);
22040     } 
22041
22042 });
22043 /**
22044  * @class Roo.bootstrap.PopoverNav
22045  * @extends Roo.bootstrap.nav.Simplebar
22046  * @parent Roo.bootstrap.Popover
22047  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22048  * @licence LGPL
22049  * Bootstrap Popover header navigation class
22050  * FIXME? should this go under nav?
22051  *
22052  * 
22053  * @constructor
22054  * Create a new Popover Header Navigation 
22055  * @param {Object} config The config object
22056  */
22057
22058 Roo.bootstrap.PopoverNav = function(config){
22059     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22060 };
22061
22062 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22063     
22064     
22065     container_method : 'getPopoverHeader' 
22066     
22067      
22068     
22069     
22070    
22071 });
22072
22073  
22074
22075  /*
22076  * - LGPL
22077  *
22078  * Progress
22079  * 
22080  */
22081
22082 /**
22083  * @class Roo.bootstrap.Progress
22084  * @extends Roo.bootstrap.Component
22085  * @children Roo.bootstrap.ProgressBar
22086  * Bootstrap Progress class
22087  * @cfg {Boolean} striped striped of the progress bar
22088  * @cfg {Boolean} active animated of the progress bar
22089  * 
22090  * 
22091  * @constructor
22092  * Create a new Progress
22093  * @param {Object} config The config object
22094  */
22095
22096 Roo.bootstrap.Progress = function(config){
22097     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22098 };
22099
22100 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22101     
22102     striped : false,
22103     active: false,
22104     
22105     getAutoCreate : function(){
22106         var cfg = {
22107             tag: 'div',
22108             cls: 'progress'
22109         };
22110         
22111         
22112         if(this.striped){
22113             cfg.cls += ' progress-striped';
22114         }
22115       
22116         if(this.active){
22117             cfg.cls += ' active';
22118         }
22119         
22120         
22121         return cfg;
22122     }
22123    
22124 });
22125
22126  
22127
22128  /*
22129  * - LGPL
22130  *
22131  * ProgressBar
22132  * 
22133  */
22134
22135 /**
22136  * @class Roo.bootstrap.ProgressBar
22137  * @extends Roo.bootstrap.Component
22138  * Bootstrap ProgressBar class
22139  * @cfg {Number} aria_valuenow aria-value now
22140  * @cfg {Number} aria_valuemin aria-value min
22141  * @cfg {Number} aria_valuemax aria-value max
22142  * @cfg {String} label label for the progress bar
22143  * @cfg {String} panel (success | info | warning | danger )
22144  * @cfg {String} role role of the progress bar
22145  * @cfg {String} sr_only text
22146  * 
22147  * 
22148  * @constructor
22149  * Create a new ProgressBar
22150  * @param {Object} config The config object
22151  */
22152
22153 Roo.bootstrap.ProgressBar = function(config){
22154     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22155 };
22156
22157 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22158     
22159     aria_valuenow : 0,
22160     aria_valuemin : 0,
22161     aria_valuemax : 100,
22162     label : false,
22163     panel : false,
22164     role : false,
22165     sr_only: false,
22166     
22167     getAutoCreate : function()
22168     {
22169         
22170         var cfg = {
22171             tag: 'div',
22172             cls: 'progress-bar',
22173             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22174         };
22175         
22176         if(this.sr_only){
22177             cfg.cn = {
22178                 tag: 'span',
22179                 cls: 'sr-only',
22180                 html: this.sr_only
22181             }
22182         }
22183         
22184         if(this.role){
22185             cfg.role = this.role;
22186         }
22187         
22188         if(this.aria_valuenow){
22189             cfg['aria-valuenow'] = this.aria_valuenow;
22190         }
22191         
22192         if(this.aria_valuemin){
22193             cfg['aria-valuemin'] = this.aria_valuemin;
22194         }
22195         
22196         if(this.aria_valuemax){
22197             cfg['aria-valuemax'] = this.aria_valuemax;
22198         }
22199         
22200         if(this.label && !this.sr_only){
22201             cfg.html = this.label;
22202         }
22203         
22204         if(this.panel){
22205             cfg.cls += ' progress-bar-' + this.panel;
22206         }
22207         
22208         return cfg;
22209     },
22210     
22211     update : function(aria_valuenow)
22212     {
22213         this.aria_valuenow = aria_valuenow;
22214         
22215         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22216     }
22217    
22218 });
22219
22220  
22221
22222  /**
22223  * @class Roo.bootstrap.TabGroup
22224  * @extends Roo.bootstrap.Column
22225  * @children Roo.bootstrap.TabPanel
22226  * Bootstrap Column class
22227  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22228  * @cfg {Boolean} carousel true to make the group behave like a carousel
22229  * @cfg {Boolean} bullets show bullets for the panels
22230  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22231  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22232  * @cfg {Boolean} showarrow (true|false) show arrow default true
22233  * 
22234  * @constructor
22235  * Create a new TabGroup
22236  * @param {Object} config The config object
22237  */
22238
22239 Roo.bootstrap.TabGroup = function(config){
22240     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22241     if (!this.navId) {
22242         this.navId = Roo.id();
22243     }
22244     this.tabs = [];
22245     Roo.bootstrap.TabGroup.register(this);
22246     
22247 };
22248
22249 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22250     
22251     carousel : false,
22252     transition : false,
22253     bullets : 0,
22254     timer : 0,
22255     autoslide : false,
22256     slideFn : false,
22257     slideOnTouch : false,
22258     showarrow : true,
22259     
22260     getAutoCreate : function()
22261     {
22262         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22263         
22264         cfg.cls += ' tab-content';
22265         
22266         if (this.carousel) {
22267             cfg.cls += ' carousel slide';
22268             
22269             cfg.cn = [{
22270                cls : 'carousel-inner',
22271                cn : []
22272             }];
22273         
22274             if(this.bullets  && !Roo.isTouch){
22275                 
22276                 var bullets = {
22277                     cls : 'carousel-bullets',
22278                     cn : []
22279                 };
22280                
22281                 if(this.bullets_cls){
22282                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22283                 }
22284                 
22285                 bullets.cn.push({
22286                     cls : 'clear'
22287                 });
22288                 
22289                 cfg.cn[0].cn.push(bullets);
22290             }
22291             
22292             if(this.showarrow){
22293                 cfg.cn[0].cn.push({
22294                     tag : 'div',
22295                     class : 'carousel-arrow',
22296                     cn : [
22297                         {
22298                             tag : 'div',
22299                             class : 'carousel-prev',
22300                             cn : [
22301                                 {
22302                                     tag : 'i',
22303                                     class : 'fa fa-chevron-left'
22304                                 }
22305                             ]
22306                         },
22307                         {
22308                             tag : 'div',
22309                             class : 'carousel-next',
22310                             cn : [
22311                                 {
22312                                     tag : 'i',
22313                                     class : 'fa fa-chevron-right'
22314                                 }
22315                             ]
22316                         }
22317                     ]
22318                 });
22319             }
22320             
22321         }
22322         
22323         return cfg;
22324     },
22325     
22326     initEvents:  function()
22327     {
22328 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22329 //            this.el.on("touchstart", this.onTouchStart, this);
22330 //        }
22331         
22332         if(this.autoslide){
22333             var _this = this;
22334             
22335             this.slideFn = window.setInterval(function() {
22336                 _this.showPanelNext();
22337             }, this.timer);
22338         }
22339         
22340         if(this.showarrow){
22341             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22342             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22343         }
22344         
22345         
22346     },
22347     
22348 //    onTouchStart : function(e, el, o)
22349 //    {
22350 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22351 //            return;
22352 //        }
22353 //        
22354 //        this.showPanelNext();
22355 //    },
22356     
22357     
22358     getChildContainer : function()
22359     {
22360         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22361     },
22362     
22363     /**
22364     * register a Navigation item
22365     * @param {Roo.bootstrap.nav.Item} the navitem to add
22366     */
22367     register : function(item)
22368     {
22369         this.tabs.push( item);
22370         item.navId = this.navId; // not really needed..
22371         this.addBullet();
22372     
22373     },
22374     
22375     getActivePanel : function()
22376     {
22377         var r = false;
22378         Roo.each(this.tabs, function(t) {
22379             if (t.active) {
22380                 r = t;
22381                 return false;
22382             }
22383             return null;
22384         });
22385         return r;
22386         
22387     },
22388     getPanelByName : function(n)
22389     {
22390         var r = false;
22391         Roo.each(this.tabs, function(t) {
22392             if (t.tabId == n) {
22393                 r = t;
22394                 return false;
22395             }
22396             return null;
22397         });
22398         return r;
22399     },
22400     indexOfPanel : function(p)
22401     {
22402         var r = false;
22403         Roo.each(this.tabs, function(t,i) {
22404             if (t.tabId == p.tabId) {
22405                 r = i;
22406                 return false;
22407             }
22408             return null;
22409         });
22410         return r;
22411     },
22412     /**
22413      * show a specific panel
22414      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22415      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22416      */
22417     showPanel : function (pan)
22418     {
22419         if(this.transition || typeof(pan) == 'undefined'){
22420             Roo.log("waiting for the transitionend");
22421             return false;
22422         }
22423         
22424         if (typeof(pan) == 'number') {
22425             pan = this.tabs[pan];
22426         }
22427         
22428         if (typeof(pan) == 'string') {
22429             pan = this.getPanelByName(pan);
22430         }
22431         
22432         var cur = this.getActivePanel();
22433         
22434         if(!pan || !cur){
22435             Roo.log('pan or acitve pan is undefined');
22436             return false;
22437         }
22438         
22439         if (pan.tabId == this.getActivePanel().tabId) {
22440             return true;
22441         }
22442         
22443         if (false === cur.fireEvent('beforedeactivate')) {
22444             return false;
22445         }
22446         
22447         if(this.bullets > 0 && !Roo.isTouch){
22448             this.setActiveBullet(this.indexOfPanel(pan));
22449         }
22450         
22451         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22452             
22453             //class="carousel-item carousel-item-next carousel-item-left"
22454             
22455             this.transition = true;
22456             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22457             var lr = dir == 'next' ? 'left' : 'right';
22458             pan.el.addClass(dir); // or prev
22459             pan.el.addClass('carousel-item-' + dir); // or prev
22460             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22461             cur.el.addClass(lr); // or right
22462             pan.el.addClass(lr);
22463             cur.el.addClass('carousel-item-' +lr); // or right
22464             pan.el.addClass('carousel-item-' +lr);
22465             
22466             
22467             var _this = this;
22468             cur.el.on('transitionend', function() {
22469                 Roo.log("trans end?");
22470                 
22471                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22472                 pan.setActive(true);
22473                 
22474                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22475                 cur.setActive(false);
22476                 
22477                 _this.transition = false;
22478                 
22479             }, this, { single:  true } );
22480             
22481             return true;
22482         }
22483         
22484         cur.setActive(false);
22485         pan.setActive(true);
22486         
22487         return true;
22488         
22489     },
22490     showPanelNext : function()
22491     {
22492         var i = this.indexOfPanel(this.getActivePanel());
22493         
22494         if (i >= this.tabs.length - 1 && !this.autoslide) {
22495             return;
22496         }
22497         
22498         if (i >= this.tabs.length - 1 && this.autoslide) {
22499             i = -1;
22500         }
22501         
22502         this.showPanel(this.tabs[i+1]);
22503     },
22504     
22505     showPanelPrev : function()
22506     {
22507         var i = this.indexOfPanel(this.getActivePanel());
22508         
22509         if (i  < 1 && !this.autoslide) {
22510             return;
22511         }
22512         
22513         if (i < 1 && this.autoslide) {
22514             i = this.tabs.length;
22515         }
22516         
22517         this.showPanel(this.tabs[i-1]);
22518     },
22519     
22520     
22521     addBullet: function()
22522     {
22523         if(!this.bullets || Roo.isTouch){
22524             return;
22525         }
22526         var ctr = this.el.select('.carousel-bullets',true).first();
22527         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22528         var bullet = ctr.createChild({
22529             cls : 'bullet bullet-' + i
22530         },ctr.dom.lastChild);
22531         
22532         
22533         var _this = this;
22534         
22535         bullet.on('click', (function(e, el, o, ii, t){
22536
22537             e.preventDefault();
22538
22539             this.showPanel(ii);
22540
22541             if(this.autoslide && this.slideFn){
22542                 clearInterval(this.slideFn);
22543                 this.slideFn = window.setInterval(function() {
22544                     _this.showPanelNext();
22545                 }, this.timer);
22546             }
22547
22548         }).createDelegate(this, [i, bullet], true));
22549                 
22550         
22551     },
22552      
22553     setActiveBullet : function(i)
22554     {
22555         if(Roo.isTouch){
22556             return;
22557         }
22558         
22559         Roo.each(this.el.select('.bullet', true).elements, function(el){
22560             el.removeClass('selected');
22561         });
22562
22563         var bullet = this.el.select('.bullet-' + i, true).first();
22564         
22565         if(!bullet){
22566             return;
22567         }
22568         
22569         bullet.addClass('selected');
22570     }
22571     
22572     
22573   
22574 });
22575
22576  
22577
22578  
22579  
22580 Roo.apply(Roo.bootstrap.TabGroup, {
22581     
22582     groups: {},
22583      /**
22584     * register a Navigation Group
22585     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22586     */
22587     register : function(navgrp)
22588     {
22589         this.groups[navgrp.navId] = navgrp;
22590         
22591     },
22592     /**
22593     * fetch a Navigation Group based on the navigation ID
22594     * if one does not exist , it will get created.
22595     * @param {string} the navgroup to add
22596     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22597     */
22598     get: function(navId) {
22599         if (typeof(this.groups[navId]) == 'undefined') {
22600             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22601         }
22602         return this.groups[navId] ;
22603     }
22604     
22605     
22606     
22607 });
22608
22609  /*
22610  * - LGPL
22611  *
22612  * TabPanel
22613  * 
22614  */
22615
22616 /**
22617  * @class Roo.bootstrap.TabPanel
22618  * @extends Roo.bootstrap.Component
22619  * @children Roo.bootstrap.Component
22620  * Bootstrap TabPanel class
22621  * @cfg {Boolean} active panel active
22622  * @cfg {String} html panel content
22623  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22624  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22625  * @cfg {String} href click to link..
22626  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22627  * 
22628  * 
22629  * @constructor
22630  * Create a new TabPanel
22631  * @param {Object} config The config object
22632  */
22633
22634 Roo.bootstrap.TabPanel = function(config){
22635     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22636     this.addEvents({
22637         /**
22638              * @event changed
22639              * Fires when the active status changes
22640              * @param {Roo.bootstrap.TabPanel} this
22641              * @param {Boolean} state the new state
22642             
22643          */
22644         'changed': true,
22645         /**
22646              * @event beforedeactivate
22647              * Fires before a tab is de-activated - can be used to do validation on a form.
22648              * @param {Roo.bootstrap.TabPanel} this
22649              * @return {Boolean} false if there is an error
22650             
22651          */
22652         'beforedeactivate': true
22653      });
22654     
22655     this.tabId = this.tabId || Roo.id();
22656   
22657 };
22658
22659 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22660     
22661     active: false,
22662     html: false,
22663     tabId: false,
22664     navId : false,
22665     href : '',
22666     touchSlide : false,
22667     getAutoCreate : function(){
22668         
22669         
22670         var cfg = {
22671             tag: 'div',
22672             // item is needed for carousel - not sure if it has any effect otherwise
22673             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22674             html: this.html || ''
22675         };
22676         
22677         if(this.active){
22678             cfg.cls += ' active';
22679         }
22680         
22681         if(this.tabId){
22682             cfg.tabId = this.tabId;
22683         }
22684         
22685         
22686         
22687         return cfg;
22688     },
22689     
22690     initEvents:  function()
22691     {
22692         var p = this.parent();
22693         
22694         this.navId = this.navId || p.navId;
22695         
22696         if (typeof(this.navId) != 'undefined') {
22697             // not really needed.. but just in case.. parent should be a NavGroup.
22698             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22699             
22700             tg.register(this);
22701             
22702             var i = tg.tabs.length - 1;
22703             
22704             if(this.active && tg.bullets > 0 && i < tg.bullets){
22705                 tg.setActiveBullet(i);
22706             }
22707         }
22708         
22709         this.el.on('click', this.onClick, this);
22710         
22711         if(Roo.isTouch && this.touchSlide){
22712             this.el.on("touchstart", this.onTouchStart, this);
22713             this.el.on("touchmove", this.onTouchMove, this);
22714             this.el.on("touchend", this.onTouchEnd, this);
22715         }
22716         
22717     },
22718     
22719     onRender : function(ct, position)
22720     {
22721         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22722     },
22723     
22724     setActive : function(state)
22725     {
22726         Roo.log("panel - set active " + this.tabId + "=" + state);
22727         
22728         this.active = state;
22729         if (!state) {
22730             this.el.removeClass('active');
22731             
22732         } else  if (!this.el.hasClass('active')) {
22733             this.el.addClass('active');
22734         }
22735         
22736         this.fireEvent('changed', this, state);
22737     },
22738     
22739     onClick : function(e)
22740     {
22741         e.preventDefault();
22742         
22743         if(!this.href.length){
22744             return;
22745         }
22746         
22747         window.location.href = this.href;
22748     },
22749     
22750     startX : 0,
22751     startY : 0,
22752     endX : 0,
22753     endY : 0,
22754     swiping : false,
22755     
22756     onTouchStart : function(e)
22757     {
22758         this.swiping = false;
22759         
22760         this.startX = e.browserEvent.touches[0].clientX;
22761         this.startY = e.browserEvent.touches[0].clientY;
22762     },
22763     
22764     onTouchMove : function(e)
22765     {
22766         this.swiping = true;
22767         
22768         this.endX = e.browserEvent.touches[0].clientX;
22769         this.endY = e.browserEvent.touches[0].clientY;
22770     },
22771     
22772     onTouchEnd : function(e)
22773     {
22774         if(!this.swiping){
22775             this.onClick(e);
22776             return;
22777         }
22778         
22779         var tabGroup = this.parent();
22780         
22781         if(this.endX > this.startX){ // swiping right
22782             tabGroup.showPanelPrev();
22783             return;
22784         }
22785         
22786         if(this.startX > this.endX){ // swiping left
22787             tabGroup.showPanelNext();
22788             return;
22789         }
22790     }
22791     
22792     
22793 });
22794  
22795
22796  
22797
22798  /*
22799  * - LGPL
22800  *
22801  * DateField
22802  * 
22803  */
22804
22805 /**
22806  * @class Roo.bootstrap.form.DateField
22807  * @extends Roo.bootstrap.form.Input
22808  * Bootstrap DateField class
22809  * @cfg {Number} weekStart default 0
22810  * @cfg {String} viewMode default empty, (months|years)
22811  * @cfg {String} minViewMode default empty, (months|years)
22812  * @cfg {Number} startDate default -Infinity
22813  * @cfg {Number} endDate default Infinity
22814  * @cfg {Boolean} todayHighlight default false
22815  * @cfg {Boolean} todayBtn default false
22816  * @cfg {Boolean} calendarWeeks default false
22817  * @cfg {Object} daysOfWeekDisabled default empty
22818  * @cfg {Boolean} singleMode default false (true | false)
22819  * 
22820  * @cfg {Boolean} keyboardNavigation default true
22821  * @cfg {String} language default en
22822  * 
22823  * @constructor
22824  * Create a new DateField
22825  * @param {Object} config The config object
22826  */
22827
22828 Roo.bootstrap.form.DateField = function(config){
22829     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22830      this.addEvents({
22831             /**
22832              * @event show
22833              * Fires when this field show.
22834              * @param {Roo.bootstrap.form.DateField} this
22835              * @param {Mixed} date The date value
22836              */
22837             show : true,
22838             /**
22839              * @event show
22840              * Fires when this field hide.
22841              * @param {Roo.bootstrap.form.DateField} this
22842              * @param {Mixed} date The date value
22843              */
22844             hide : true,
22845             /**
22846              * @event select
22847              * Fires when select a date.
22848              * @param {Roo.bootstrap.form.DateField} this
22849              * @param {Mixed} date The date value
22850              */
22851             select : true,
22852             /**
22853              * @event beforeselect
22854              * Fires when before select a date.
22855              * @param {Roo.bootstrap.form.DateField} this
22856              * @param {Mixed} date The date value
22857              */
22858             beforeselect : true
22859         });
22860 };
22861
22862 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22863     
22864     /**
22865      * @cfg {String} format
22866      * The default date format string which can be overriden for localization support.  The format must be
22867      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22868      */
22869     format : "m/d/y",
22870     /**
22871      * @cfg {String} altFormats
22872      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22873      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22874      */
22875     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22876     
22877     weekStart : 0,
22878     
22879     viewMode : '',
22880     
22881     minViewMode : '',
22882     
22883     todayHighlight : false,
22884     
22885     todayBtn: false,
22886     
22887     language: 'en',
22888     
22889     keyboardNavigation: true,
22890     
22891     calendarWeeks: false,
22892     
22893     startDate: -Infinity,
22894     
22895     endDate: Infinity,
22896     
22897     daysOfWeekDisabled: [],
22898     
22899     _events: [],
22900     
22901     singleMode : false,
22902     
22903     UTCDate: function()
22904     {
22905         return new Date(Date.UTC.apply(Date, arguments));
22906     },
22907     
22908     UTCToday: function()
22909     {
22910         var today = new Date();
22911         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22912     },
22913     
22914     getDate: function() {
22915             var d = this.getUTCDate();
22916             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22917     },
22918     
22919     getUTCDate: function() {
22920             return this.date;
22921     },
22922     
22923     setDate: function(d) {
22924             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22925     },
22926     
22927     setUTCDate: function(d) {
22928             this.date = d;
22929             this.setValue(this.formatDate(this.date));
22930     },
22931         
22932     onRender: function(ct, position)
22933     {
22934         
22935         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22936         
22937         this.language = this.language || 'en';
22938         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22939         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22940         
22941         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22942         this.format = this.format || 'm/d/y';
22943         this.isInline = false;
22944         this.isInput = true;
22945         this.component = this.el.select('.add-on', true).first() || false;
22946         this.component = (this.component && this.component.length === 0) ? false : this.component;
22947         this.hasInput = this.component && this.inputEl().length;
22948         
22949         if (typeof(this.minViewMode === 'string')) {
22950             switch (this.minViewMode) {
22951                 case 'months':
22952                     this.minViewMode = 1;
22953                     break;
22954                 case 'years':
22955                     this.minViewMode = 2;
22956                     break;
22957                 default:
22958                     this.minViewMode = 0;
22959                     break;
22960             }
22961         }
22962         
22963         if (typeof(this.viewMode === 'string')) {
22964             switch (this.viewMode) {
22965                 case 'months':
22966                     this.viewMode = 1;
22967                     break;
22968                 case 'years':
22969                     this.viewMode = 2;
22970                     break;
22971                 default:
22972                     this.viewMode = 0;
22973                     break;
22974             }
22975         }
22976                 
22977         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22978         
22979 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22980         
22981         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22982         
22983         this.picker().on('mousedown', this.onMousedown, this);
22984         this.picker().on('click', this.onClick, this);
22985         
22986         this.picker().addClass('datepicker-dropdown');
22987         
22988         this.startViewMode = this.viewMode;
22989         
22990         if(this.singleMode){
22991             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22992                 v.setVisibilityMode(Roo.Element.DISPLAY);
22993                 v.hide();
22994             });
22995             
22996             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22997                 v.setStyle('width', '189px');
22998             });
22999         }
23000         
23001         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23002             if(!this.calendarWeeks){
23003                 v.remove();
23004                 return;
23005             }
23006             
23007             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23008             v.attr('colspan', function(i, val){
23009                 return parseInt(val) + 1;
23010             });
23011         });
23012                         
23013         
23014         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23015         
23016         this.setStartDate(this.startDate);
23017         this.setEndDate(this.endDate);
23018         
23019         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23020         
23021         this.fillDow();
23022         this.fillMonths();
23023         this.update();
23024         this.showMode();
23025         
23026         if(this.isInline) {
23027             this.showPopup();
23028         }
23029     },
23030     
23031     picker : function()
23032     {
23033         return this.pickerEl;
23034 //        return this.el.select('.datepicker', true).first();
23035     },
23036     
23037     fillDow: function()
23038     {
23039         var dowCnt = this.weekStart;
23040         
23041         var dow = {
23042             tag: 'tr',
23043             cn: [
23044                 
23045             ]
23046         };
23047         
23048         if(this.calendarWeeks){
23049             dow.cn.push({
23050                 tag: 'th',
23051                 cls: 'cw',
23052                 html: '&nbsp;'
23053             })
23054         }
23055         
23056         while (dowCnt < this.weekStart + 7) {
23057             dow.cn.push({
23058                 tag: 'th',
23059                 cls: 'dow',
23060                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23061             });
23062         }
23063         
23064         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23065     },
23066     
23067     fillMonths: function()
23068     {    
23069         var i = 0;
23070         var months = this.picker().select('>.datepicker-months td', true).first();
23071         
23072         months.dom.innerHTML = '';
23073         
23074         while (i < 12) {
23075             var month = {
23076                 tag: 'span',
23077                 cls: 'month',
23078                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23079             };
23080             
23081             months.createChild(month);
23082         }
23083         
23084     },
23085     
23086     update: function()
23087     {
23088         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;
23089         
23090         if (this.date < this.startDate) {
23091             this.viewDate = new Date(this.startDate);
23092         } else if (this.date > this.endDate) {
23093             this.viewDate = new Date(this.endDate);
23094         } else {
23095             this.viewDate = new Date(this.date);
23096         }
23097         
23098         this.fill();
23099     },
23100     
23101     fill: function() 
23102     {
23103         var d = new Date(this.viewDate),
23104                 year = d.getUTCFullYear(),
23105                 month = d.getUTCMonth(),
23106                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23107                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23108                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23109                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23110                 currentDate = this.date && this.date.valueOf(),
23111                 today = this.UTCToday();
23112         
23113         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23114         
23115 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23116         
23117 //        this.picker.select('>tfoot th.today').
23118 //                                              .text(dates[this.language].today)
23119 //                                              .toggle(this.todayBtn !== false);
23120     
23121         this.updateNavArrows();
23122         this.fillMonths();
23123                                                 
23124         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23125         
23126         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23127          
23128         prevMonth.setUTCDate(day);
23129         
23130         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23131         
23132         var nextMonth = new Date(prevMonth);
23133         
23134         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23135         
23136         nextMonth = nextMonth.valueOf();
23137         
23138         var fillMonths = false;
23139         
23140         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23141         
23142         while(prevMonth.valueOf() <= nextMonth) {
23143             var clsName = '';
23144             
23145             if (prevMonth.getUTCDay() === this.weekStart) {
23146                 if(fillMonths){
23147                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23148                 }
23149                     
23150                 fillMonths = {
23151                     tag: 'tr',
23152                     cn: []
23153                 };
23154                 
23155                 if(this.calendarWeeks){
23156                     // ISO 8601: First week contains first thursday.
23157                     // ISO also states week starts on Monday, but we can be more abstract here.
23158                     var
23159                     // Start of current week: based on weekstart/current date
23160                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23161                     // Thursday of this week
23162                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23163                     // First Thursday of year, year from thursday
23164                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23165                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23166                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23167                     
23168                     fillMonths.cn.push({
23169                         tag: 'td',
23170                         cls: 'cw',
23171                         html: calWeek
23172                     });
23173                 }
23174             }
23175             
23176             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23177                 clsName += ' old';
23178             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23179                 clsName += ' new';
23180             }
23181             if (this.todayHighlight &&
23182                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23183                 prevMonth.getUTCMonth() == today.getMonth() &&
23184                 prevMonth.getUTCDate() == today.getDate()) {
23185                 clsName += ' today';
23186             }
23187             
23188             if (currentDate && prevMonth.valueOf() === currentDate) {
23189                 clsName += ' active';
23190             }
23191             
23192             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23193                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23194                     clsName += ' disabled';
23195             }
23196             
23197             fillMonths.cn.push({
23198                 tag: 'td',
23199                 cls: 'day ' + clsName,
23200                 html: prevMonth.getDate()
23201             });
23202             
23203             prevMonth.setDate(prevMonth.getDate()+1);
23204         }
23205           
23206         var currentYear = this.date && this.date.getUTCFullYear();
23207         var currentMonth = this.date && this.date.getUTCMonth();
23208         
23209         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23210         
23211         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23212             v.removeClass('active');
23213             
23214             if(currentYear === year && k === currentMonth){
23215                 v.addClass('active');
23216             }
23217             
23218             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23219                 v.addClass('disabled');
23220             }
23221             
23222         });
23223         
23224         
23225         year = parseInt(year/10, 10) * 10;
23226         
23227         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23228         
23229         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23230         
23231         year -= 1;
23232         for (var i = -1; i < 11; i++) {
23233             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23234                 tag: 'span',
23235                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23236                 html: year
23237             });
23238             
23239             year += 1;
23240         }
23241     },
23242     
23243     showMode: function(dir) 
23244     {
23245         if (dir) {
23246             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23247         }
23248         
23249         Roo.each(this.picker().select('>div',true).elements, function(v){
23250             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23251             v.hide();
23252         });
23253         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23254     },
23255     
23256     place: function()
23257     {
23258         if(this.isInline) {
23259             return;
23260         }
23261         
23262         this.picker().removeClass(['bottom', 'top']);
23263         
23264         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23265             /*
23266              * place to the top of element!
23267              *
23268              */
23269             
23270             this.picker().addClass('top');
23271             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23272             
23273             return;
23274         }
23275         
23276         this.picker().addClass('bottom');
23277         
23278         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23279     },
23280     
23281     parseDate : function(value)
23282     {
23283         if(!value || value instanceof Date){
23284             return value;
23285         }
23286         var v = Date.parseDate(value, this.format);
23287         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23288             v = Date.parseDate(value, 'Y-m-d');
23289         }
23290         if(!v && this.altFormats){
23291             if(!this.altFormatsArray){
23292                 this.altFormatsArray = this.altFormats.split("|");
23293             }
23294             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23295                 v = Date.parseDate(value, this.altFormatsArray[i]);
23296             }
23297         }
23298         return v;
23299     },
23300     
23301     formatDate : function(date, fmt)
23302     {   
23303         return (!date || !(date instanceof Date)) ?
23304         date : date.dateFormat(fmt || this.format);
23305     },
23306     
23307     onFocus : function()
23308     {
23309         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23310         this.showPopup();
23311     },
23312     
23313     onBlur : function()
23314     {
23315         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23316         
23317         var d = this.inputEl().getValue();
23318         
23319         this.setValue(d);
23320                 
23321         this.hidePopup();
23322     },
23323     
23324     showPopup : function()
23325     {
23326         this.picker().show();
23327         this.update();
23328         this.place();
23329         
23330         this.fireEvent('showpopup', this, this.date);
23331     },
23332     
23333     hidePopup : function()
23334     {
23335         if(this.isInline) {
23336             return;
23337         }
23338         this.picker().hide();
23339         this.viewMode = this.startViewMode;
23340         this.showMode();
23341         
23342         this.fireEvent('hidepopup', this, this.date);
23343         
23344     },
23345     
23346     onMousedown: function(e)
23347     {
23348         e.stopPropagation();
23349         e.preventDefault();
23350     },
23351     
23352     keyup: function(e)
23353     {
23354         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23355         this.update();
23356     },
23357
23358     setValue: function(v)
23359     {
23360         if(this.fireEvent('beforeselect', this, v) !== false){
23361             var d = new Date(this.parseDate(v) ).clearTime();
23362         
23363             if(isNaN(d.getTime())){
23364                 this.date = this.viewDate = '';
23365                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23366                 return;
23367             }
23368
23369             v = this.formatDate(d);
23370
23371             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23372
23373             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23374
23375             this.update();
23376
23377             this.fireEvent('select', this, this.date);
23378         }
23379     },
23380     
23381     getValue: function()
23382     {
23383         return this.formatDate(this.date);
23384     },
23385     
23386     fireKey: function(e)
23387     {
23388         if (!this.picker().isVisible()){
23389             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23390                 this.showPopup();
23391             }
23392             return;
23393         }
23394         
23395         var dateChanged = false,
23396         dir, day, month,
23397         newDate, newViewDate;
23398         
23399         switch(e.keyCode){
23400             case 27: // escape
23401                 this.hidePopup();
23402                 e.preventDefault();
23403                 break;
23404             case 37: // left
23405             case 39: // right
23406                 if (!this.keyboardNavigation) {
23407                     break;
23408                 }
23409                 dir = e.keyCode == 37 ? -1 : 1;
23410                 
23411                 if (e.ctrlKey){
23412                     newDate = this.moveYear(this.date, dir);
23413                     newViewDate = this.moveYear(this.viewDate, dir);
23414                 } else if (e.shiftKey){
23415                     newDate = this.moveMonth(this.date, dir);
23416                     newViewDate = this.moveMonth(this.viewDate, dir);
23417                 } else {
23418                     newDate = new Date(this.date);
23419                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23420                     newViewDate = new Date(this.viewDate);
23421                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23422                 }
23423                 if (this.dateWithinRange(newDate)){
23424                     this.date = newDate;
23425                     this.viewDate = newViewDate;
23426                     this.setValue(this.formatDate(this.date));
23427 //                    this.update();
23428                     e.preventDefault();
23429                     dateChanged = true;
23430                 }
23431                 break;
23432             case 38: // up
23433             case 40: // down
23434                 if (!this.keyboardNavigation) {
23435                     break;
23436                 }
23437                 dir = e.keyCode == 38 ? -1 : 1;
23438                 if (e.ctrlKey){
23439                     newDate = this.moveYear(this.date, dir);
23440                     newViewDate = this.moveYear(this.viewDate, dir);
23441                 } else if (e.shiftKey){
23442                     newDate = this.moveMonth(this.date, dir);
23443                     newViewDate = this.moveMonth(this.viewDate, dir);
23444                 } else {
23445                     newDate = new Date(this.date);
23446                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23447                     newViewDate = new Date(this.viewDate);
23448                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23449                 }
23450                 if (this.dateWithinRange(newDate)){
23451                     this.date = newDate;
23452                     this.viewDate = newViewDate;
23453                     this.setValue(this.formatDate(this.date));
23454 //                    this.update();
23455                     e.preventDefault();
23456                     dateChanged = true;
23457                 }
23458                 break;
23459             case 13: // enter
23460                 this.setValue(this.formatDate(this.date));
23461                 this.hidePopup();
23462                 e.preventDefault();
23463                 break;
23464             case 9: // tab
23465                 this.setValue(this.formatDate(this.date));
23466                 this.hidePopup();
23467                 break;
23468             case 16: // shift
23469             case 17: // ctrl
23470             case 18: // alt
23471                 break;
23472             default :
23473                 this.hidePopup();
23474                 
23475         }
23476     },
23477     
23478     
23479     onClick: function(e) 
23480     {
23481         e.stopPropagation();
23482         e.preventDefault();
23483         
23484         var target = e.getTarget();
23485         
23486         if(target.nodeName.toLowerCase() === 'i'){
23487             target = Roo.get(target).dom.parentNode;
23488         }
23489         
23490         var nodeName = target.nodeName;
23491         var className = target.className;
23492         var html = target.innerHTML;
23493         //Roo.log(nodeName);
23494         
23495         switch(nodeName.toLowerCase()) {
23496             case 'th':
23497                 switch(className) {
23498                     case 'switch':
23499                         this.showMode(1);
23500                         break;
23501                     case 'prev':
23502                     case 'next':
23503                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23504                         switch(this.viewMode){
23505                                 case 0:
23506                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23507                                         break;
23508                                 case 1:
23509                                 case 2:
23510                                         this.viewDate = this.moveYear(this.viewDate, dir);
23511                                         break;
23512                         }
23513                         this.fill();
23514                         break;
23515                     case 'today':
23516                         var date = new Date();
23517                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23518 //                        this.fill()
23519                         this.setValue(this.formatDate(this.date));
23520                         
23521                         this.hidePopup();
23522                         break;
23523                 }
23524                 break;
23525             case 'span':
23526                 if (className.indexOf('disabled') < 0) {
23527                 if (!this.viewDate) {
23528                     this.viewDate = new Date();
23529                 }
23530                 this.viewDate.setUTCDate(1);
23531                     if (className.indexOf('month') > -1) {
23532                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23533                     } else {
23534                         var year = parseInt(html, 10) || 0;
23535                         this.viewDate.setUTCFullYear(year);
23536                         
23537                     }
23538                     
23539                     if(this.singleMode){
23540                         this.setValue(this.formatDate(this.viewDate));
23541                         this.hidePopup();
23542                         return;
23543                     }
23544                     
23545                     this.showMode(-1);
23546                     this.fill();
23547                 }
23548                 break;
23549                 
23550             case 'td':
23551                 //Roo.log(className);
23552                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23553                     var day = parseInt(html, 10) || 1;
23554                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23555                         month = (this.viewDate || new Date()).getUTCMonth();
23556
23557                     if (className.indexOf('old') > -1) {
23558                         if(month === 0 ){
23559                             month = 11;
23560                             year -= 1;
23561                         }else{
23562                             month -= 1;
23563                         }
23564                     } else if (className.indexOf('new') > -1) {
23565                         if (month == 11) {
23566                             month = 0;
23567                             year += 1;
23568                         } else {
23569                             month += 1;
23570                         }
23571                     }
23572                     //Roo.log([year,month,day]);
23573                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23574                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23575 //                    this.fill();
23576                     //Roo.log(this.formatDate(this.date));
23577                     this.setValue(this.formatDate(this.date));
23578                     this.hidePopup();
23579                 }
23580                 break;
23581         }
23582     },
23583     
23584     setStartDate: function(startDate)
23585     {
23586         this.startDate = startDate || -Infinity;
23587         if (this.startDate !== -Infinity) {
23588             this.startDate = this.parseDate(this.startDate);
23589         }
23590         this.update();
23591         this.updateNavArrows();
23592     },
23593
23594     setEndDate: function(endDate)
23595     {
23596         this.endDate = endDate || Infinity;
23597         if (this.endDate !== Infinity) {
23598             this.endDate = this.parseDate(this.endDate);
23599         }
23600         this.update();
23601         this.updateNavArrows();
23602     },
23603     
23604     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23605     {
23606         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23607         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23608             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23609         }
23610         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23611             return parseInt(d, 10);
23612         });
23613         this.update();
23614         this.updateNavArrows();
23615     },
23616     
23617     updateNavArrows: function() 
23618     {
23619         if(this.singleMode){
23620             return;
23621         }
23622         
23623         var d = new Date(this.viewDate),
23624         year = d.getUTCFullYear(),
23625         month = d.getUTCMonth();
23626         
23627         Roo.each(this.picker().select('.prev', true).elements, function(v){
23628             v.show();
23629             switch (this.viewMode) {
23630                 case 0:
23631
23632                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23633                         v.hide();
23634                     }
23635                     break;
23636                 case 1:
23637                 case 2:
23638                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23639                         v.hide();
23640                     }
23641                     break;
23642             }
23643         });
23644         
23645         Roo.each(this.picker().select('.next', true).elements, function(v){
23646             v.show();
23647             switch (this.viewMode) {
23648                 case 0:
23649
23650                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23651                         v.hide();
23652                     }
23653                     break;
23654                 case 1:
23655                 case 2:
23656                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23657                         v.hide();
23658                     }
23659                     break;
23660             }
23661         })
23662     },
23663     
23664     moveMonth: function(date, dir)
23665     {
23666         if (!dir) {
23667             return date;
23668         }
23669         var new_date = new Date(date.valueOf()),
23670         day = new_date.getUTCDate(),
23671         month = new_date.getUTCMonth(),
23672         mag = Math.abs(dir),
23673         new_month, test;
23674         dir = dir > 0 ? 1 : -1;
23675         if (mag == 1){
23676             test = dir == -1
23677             // If going back one month, make sure month is not current month
23678             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23679             ? function(){
23680                 return new_date.getUTCMonth() == month;
23681             }
23682             // If going forward one month, make sure month is as expected
23683             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23684             : function(){
23685                 return new_date.getUTCMonth() != new_month;
23686             };
23687             new_month = month + dir;
23688             new_date.setUTCMonth(new_month);
23689             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23690             if (new_month < 0 || new_month > 11) {
23691                 new_month = (new_month + 12) % 12;
23692             }
23693         } else {
23694             // For magnitudes >1, move one month at a time...
23695             for (var i=0; i<mag; i++) {
23696                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23697                 new_date = this.moveMonth(new_date, dir);
23698             }
23699             // ...then reset the day, keeping it in the new month
23700             new_month = new_date.getUTCMonth();
23701             new_date.setUTCDate(day);
23702             test = function(){
23703                 return new_month != new_date.getUTCMonth();
23704             };
23705         }
23706         // Common date-resetting loop -- if date is beyond end of month, make it
23707         // end of month
23708         while (test()){
23709             new_date.setUTCDate(--day);
23710             new_date.setUTCMonth(new_month);
23711         }
23712         return new_date;
23713     },
23714
23715     moveYear: function(date, dir)
23716     {
23717         return this.moveMonth(date, dir*12);
23718     },
23719
23720     dateWithinRange: function(date)
23721     {
23722         return date >= this.startDate && date <= this.endDate;
23723     },
23724
23725     
23726     remove: function() 
23727     {
23728         this.picker().remove();
23729     },
23730     
23731     validateValue : function(value)
23732     {
23733         if(this.getVisibilityEl().hasClass('hidden')){
23734             return true;
23735         }
23736         
23737         if(value.length < 1)  {
23738             if(this.allowBlank){
23739                 return true;
23740             }
23741             return false;
23742         }
23743         
23744         if(value.length < this.minLength){
23745             return false;
23746         }
23747         if(value.length > this.maxLength){
23748             return false;
23749         }
23750         if(this.vtype){
23751             var vt = Roo.form.VTypes;
23752             if(!vt[this.vtype](value, this)){
23753                 return false;
23754             }
23755         }
23756         if(typeof this.validator == "function"){
23757             var msg = this.validator(value);
23758             if(msg !== true){
23759                 return false;
23760             }
23761         }
23762         
23763         if(this.regex && !this.regex.test(value)){
23764             return false;
23765         }
23766         
23767         if(typeof(this.parseDate(value)) == 'undefined'){
23768             return false;
23769         }
23770         
23771         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23772             return false;
23773         }      
23774         
23775         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23776             return false;
23777         } 
23778         
23779         
23780         return true;
23781     },
23782     
23783     reset : function()
23784     {
23785         this.date = this.viewDate = '';
23786         
23787         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23788     }
23789    
23790 });
23791
23792 Roo.apply(Roo.bootstrap.form.DateField,  {
23793     
23794     head : {
23795         tag: 'thead',
23796         cn: [
23797         {
23798             tag: 'tr',
23799             cn: [
23800             {
23801                 tag: 'th',
23802                 cls: 'prev',
23803                 html: '<i class="fa fa-arrow-left"/>'
23804             },
23805             {
23806                 tag: 'th',
23807                 cls: 'switch',
23808                 colspan: '5'
23809             },
23810             {
23811                 tag: 'th',
23812                 cls: 'next',
23813                 html: '<i class="fa fa-arrow-right"/>'
23814             }
23815
23816             ]
23817         }
23818         ]
23819     },
23820     
23821     content : {
23822         tag: 'tbody',
23823         cn: [
23824         {
23825             tag: 'tr',
23826             cn: [
23827             {
23828                 tag: 'td',
23829                 colspan: '7'
23830             }
23831             ]
23832         }
23833         ]
23834     },
23835     
23836     footer : {
23837         tag: 'tfoot',
23838         cn: [
23839         {
23840             tag: 'tr',
23841             cn: [
23842             {
23843                 tag: 'th',
23844                 colspan: '7',
23845                 cls: 'today'
23846             }
23847                     
23848             ]
23849         }
23850         ]
23851     },
23852     
23853     dates:{
23854         en: {
23855             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23856             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23857             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23858             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23859             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23860             today: "Today"
23861         }
23862     },
23863     
23864     modes: [
23865     {
23866         clsName: 'days',
23867         navFnc: 'Month',
23868         navStep: 1
23869     },
23870     {
23871         clsName: 'months',
23872         navFnc: 'FullYear',
23873         navStep: 1
23874     },
23875     {
23876         clsName: 'years',
23877         navFnc: 'FullYear',
23878         navStep: 10
23879     }]
23880 });
23881
23882 Roo.apply(Roo.bootstrap.form.DateField,  {
23883   
23884     template : {
23885         tag: 'div',
23886         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23887         cn: [
23888         {
23889             tag: 'div',
23890             cls: 'datepicker-days',
23891             cn: [
23892             {
23893                 tag: 'table',
23894                 cls: 'table-condensed',
23895                 cn:[
23896                 Roo.bootstrap.form.DateField.head,
23897                 {
23898                     tag: 'tbody'
23899                 },
23900                 Roo.bootstrap.form.DateField.footer
23901                 ]
23902             }
23903             ]
23904         },
23905         {
23906             tag: 'div',
23907             cls: 'datepicker-months',
23908             cn: [
23909             {
23910                 tag: 'table',
23911                 cls: 'table-condensed',
23912                 cn:[
23913                 Roo.bootstrap.form.DateField.head,
23914                 Roo.bootstrap.form.DateField.content,
23915                 Roo.bootstrap.form.DateField.footer
23916                 ]
23917             }
23918             ]
23919         },
23920         {
23921             tag: 'div',
23922             cls: 'datepicker-years',
23923             cn: [
23924             {
23925                 tag: 'table',
23926                 cls: 'table-condensed',
23927                 cn:[
23928                 Roo.bootstrap.form.DateField.head,
23929                 Roo.bootstrap.form.DateField.content,
23930                 Roo.bootstrap.form.DateField.footer
23931                 ]
23932             }
23933             ]
23934         }
23935         ]
23936     }
23937 });
23938
23939  
23940
23941  /*
23942  * - LGPL
23943  *
23944  * TimeField
23945  * 
23946  */
23947
23948 /**
23949  * @class Roo.bootstrap.form.TimeField
23950  * @extends Roo.bootstrap.form.Input
23951  * Bootstrap DateField class
23952  * 
23953  * 
23954  * @constructor
23955  * Create a new TimeField
23956  * @param {Object} config The config object
23957  */
23958
23959 Roo.bootstrap.form.TimeField = function(config){
23960     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23961     this.addEvents({
23962             /**
23963              * @event show
23964              * Fires when this field show.
23965              * @param {Roo.bootstrap.form.DateField} thisthis
23966              * @param {Mixed} date The date value
23967              */
23968             show : true,
23969             /**
23970              * @event show
23971              * Fires when this field hide.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             hide : true,
23976             /**
23977              * @event select
23978              * Fires when select a date.
23979              * @param {Roo.bootstrap.form.DateField} this
23980              * @param {Mixed} date The date value
23981              */
23982             select : true
23983         });
23984 };
23985
23986 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23987     
23988     /**
23989      * @cfg {String} format
23990      * The default time format string which can be overriden for localization support.  The format must be
23991      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23992      */
23993     format : "H:i",
23994
23995     getAutoCreate : function()
23996     {
23997         this.after = '<i class="fa far fa-clock"></i>';
23998         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23999         
24000          
24001     },
24002     onRender: function(ct, position)
24003     {
24004         
24005         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24006                 
24007         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24008         
24009         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24010         
24011         this.pop = this.picker().select('>.datepicker-time',true).first();
24012         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24013         
24014         this.picker().on('mousedown', this.onMousedown, this);
24015         this.picker().on('click', this.onClick, this);
24016         
24017         this.picker().addClass('datepicker-dropdown');
24018     
24019         this.fillTime();
24020         this.update();
24021             
24022         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24023         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24024         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24025         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24026         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24027         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24028
24029     },
24030     
24031     fireKey: function(e){
24032         if (!this.picker().isVisible()){
24033             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24034                 this.show();
24035             }
24036             return;
24037         }
24038
24039         e.preventDefault();
24040         
24041         switch(e.keyCode){
24042             case 27: // escape
24043                 this.hide();
24044                 break;
24045             case 37: // left
24046             case 39: // right
24047                 this.onTogglePeriod();
24048                 break;
24049             case 38: // up
24050                 this.onIncrementMinutes();
24051                 break;
24052             case 40: // down
24053                 this.onDecrementMinutes();
24054                 break;
24055             case 13: // enter
24056             case 9: // tab
24057                 this.setTime();
24058                 break;
24059         }
24060     },
24061     
24062     onClick: function(e) {
24063         e.stopPropagation();
24064         e.preventDefault();
24065     },
24066     
24067     picker : function()
24068     {
24069         return this.pickerEl;
24070     },
24071     
24072     fillTime: function()
24073     {    
24074         var time = this.pop.select('tbody', true).first();
24075         
24076         time.dom.innerHTML = '';
24077         
24078         time.createChild({
24079             tag: 'tr',
24080             cn: [
24081                 {
24082                     tag: 'td',
24083                     cn: [
24084                         {
24085                             tag: 'a',
24086                             href: '#',
24087                             cls: 'btn',
24088                             cn: [
24089                                 {
24090                                     tag: 'i',
24091                                     cls: 'hours-up fa fas fa-chevron-up'
24092                                 }
24093                             ]
24094                         } 
24095                     ]
24096                 },
24097                 {
24098                     tag: 'td',
24099                     cls: 'separator'
24100                 },
24101                 {
24102                     tag: 'td',
24103                     cn: [
24104                         {
24105                             tag: 'a',
24106                             href: '#',
24107                             cls: 'btn',
24108                             cn: [
24109                                 {
24110                                     tag: 'i',
24111                                     cls: 'minutes-up fa fas fa-chevron-up'
24112                                 }
24113                             ]
24114                         }
24115                     ]
24116                 },
24117                 {
24118                     tag: 'td',
24119                     cls: 'separator'
24120                 }
24121             ]
24122         });
24123         
24124         time.createChild({
24125             tag: 'tr',
24126             cn: [
24127                 {
24128                     tag: 'td',
24129                     cn: [
24130                         {
24131                             tag: 'span',
24132                             cls: 'timepicker-hour',
24133                             html: '00'
24134                         }  
24135                     ]
24136                 },
24137                 {
24138                     tag: 'td',
24139                     cls: 'separator',
24140                     html: ':'
24141                 },
24142                 {
24143                     tag: 'td',
24144                     cn: [
24145                         {
24146                             tag: 'span',
24147                             cls: 'timepicker-minute',
24148                             html: '00'
24149                         }  
24150                     ]
24151                 },
24152                 {
24153                     tag: 'td',
24154                     cls: 'separator'
24155                 },
24156                 {
24157                     tag: 'td',
24158                     cn: [
24159                         {
24160                             tag: 'button',
24161                             type: 'button',
24162                             cls: 'btn btn-primary period',
24163                             html: 'AM'
24164                             
24165                         }
24166                     ]
24167                 }
24168             ]
24169         });
24170         
24171         time.createChild({
24172             tag: 'tr',
24173             cn: [
24174                 {
24175                     tag: 'td',
24176                     cn: [
24177                         {
24178                             tag: 'a',
24179                             href: '#',
24180                             cls: 'btn',
24181                             cn: [
24182                                 {
24183                                     tag: 'span',
24184                                     cls: 'hours-down fa fas fa-chevron-down'
24185                                 }
24186                             ]
24187                         }
24188                     ]
24189                 },
24190                 {
24191                     tag: 'td',
24192                     cls: 'separator'
24193                 },
24194                 {
24195                     tag: 'td',
24196                     cn: [
24197                         {
24198                             tag: 'a',
24199                             href: '#',
24200                             cls: 'btn',
24201                             cn: [
24202                                 {
24203                                     tag: 'span',
24204                                     cls: 'minutes-down fa fas fa-chevron-down'
24205                                 }
24206                             ]
24207                         }
24208                     ]
24209                 },
24210                 {
24211                     tag: 'td',
24212                     cls: 'separator'
24213                 }
24214             ]
24215         });
24216         
24217     },
24218     
24219     update: function()
24220     {
24221         
24222         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24223         
24224         this.fill();
24225     },
24226     
24227     fill: function() 
24228     {
24229         var hours = this.time.getHours();
24230         var minutes = this.time.getMinutes();
24231         var period = 'AM';
24232         
24233         if(hours > 11){
24234             period = 'PM';
24235         }
24236         
24237         if(hours == 0){
24238             hours = 12;
24239         }
24240         
24241         
24242         if(hours > 12){
24243             hours = hours - 12;
24244         }
24245         
24246         if(hours < 10){
24247             hours = '0' + hours;
24248         }
24249         
24250         if(minutes < 10){
24251             minutes = '0' + minutes;
24252         }
24253         
24254         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24255         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24256         this.pop.select('button', true).first().dom.innerHTML = period;
24257         
24258     },
24259     
24260     place: function()
24261     {   
24262         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24263         
24264         var cls = ['bottom'];
24265         
24266         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24267             cls.pop();
24268             cls.push('top');
24269         }
24270         
24271         cls.push('right');
24272         
24273         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24274             cls.pop();
24275             cls.push('left');
24276         }
24277         //this.picker().setXY(20000,20000);
24278         this.picker().addClass(cls.join('-'));
24279         
24280         var _this = this;
24281         
24282         Roo.each(cls, function(c){
24283             if(c == 'bottom'){
24284                 (function() {
24285                  //  
24286                 }).defer(200);
24287                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24288                 //_this.picker().setTop(_this.inputEl().getHeight());
24289                 return;
24290             }
24291             if(c == 'top'){
24292                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24293                 
24294                 //_this.picker().setTop(0 - _this.picker().getHeight());
24295                 return;
24296             }
24297             /*
24298             if(c == 'left'){
24299                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24300                 return;
24301             }
24302             if(c == 'right'){
24303                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24304                 return;
24305             }
24306             */
24307         });
24308         
24309     },
24310   
24311     onFocus : function()
24312     {
24313         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24314         this.show();
24315     },
24316     
24317     onBlur : function()
24318     {
24319         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24320         this.hide();
24321     },
24322     
24323     show : function()
24324     {
24325         this.picker().show();
24326         this.pop.show();
24327         this.update();
24328         this.place();
24329         
24330         this.fireEvent('show', this, this.date);
24331     },
24332     
24333     hide : function()
24334     {
24335         this.picker().hide();
24336         this.pop.hide();
24337         
24338         this.fireEvent('hide', this, this.date);
24339     },
24340     
24341     setTime : function()
24342     {
24343         this.hide();
24344         this.setValue(this.time.format(this.format));
24345         
24346         this.fireEvent('select', this, this.date);
24347         
24348         
24349     },
24350     
24351     onMousedown: function(e){
24352         e.stopPropagation();
24353         e.preventDefault();
24354     },
24355     
24356     onIncrementHours: function()
24357     {
24358         Roo.log('onIncrementHours');
24359         this.time = this.time.add(Date.HOUR, 1);
24360         this.update();
24361         
24362     },
24363     
24364     onDecrementHours: function()
24365     {
24366         Roo.log('onDecrementHours');
24367         this.time = this.time.add(Date.HOUR, -1);
24368         this.update();
24369     },
24370     
24371     onIncrementMinutes: function()
24372     {
24373         Roo.log('onIncrementMinutes');
24374         this.time = this.time.add(Date.MINUTE, 1);
24375         this.update();
24376     },
24377     
24378     onDecrementMinutes: function()
24379     {
24380         Roo.log('onDecrementMinutes');
24381         this.time = this.time.add(Date.MINUTE, -1);
24382         this.update();
24383     },
24384     
24385     onTogglePeriod: function()
24386     {
24387         Roo.log('onTogglePeriod');
24388         this.time = this.time.add(Date.HOUR, 12);
24389         this.update();
24390     }
24391     
24392    
24393 });
24394  
24395
24396 Roo.apply(Roo.bootstrap.form.TimeField,  {
24397   
24398     template : {
24399         tag: 'div',
24400         cls: 'datepicker dropdown-menu',
24401         cn: [
24402             {
24403                 tag: 'div',
24404                 cls: 'datepicker-time',
24405                 cn: [
24406                 {
24407                     tag: 'table',
24408                     cls: 'table-condensed',
24409                     cn:[
24410                         {
24411                             tag: 'tbody',
24412                             cn: [
24413                                 {
24414                                     tag: 'tr',
24415                                     cn: [
24416                                     {
24417                                         tag: 'td',
24418                                         colspan: '7'
24419                                     }
24420                                     ]
24421                                 }
24422                             ]
24423                         },
24424                         {
24425                             tag: 'tfoot',
24426                             cn: [
24427                                 {
24428                                     tag: 'tr',
24429                                     cn: [
24430                                     {
24431                                         tag: 'th',
24432                                         colspan: '7',
24433                                         cls: '',
24434                                         cn: [
24435                                             {
24436                                                 tag: 'button',
24437                                                 cls: 'btn btn-info ok',
24438                                                 html: 'OK'
24439                                             }
24440                                         ]
24441                                     }
24442                     
24443                                     ]
24444                                 }
24445                             ]
24446                         }
24447                     ]
24448                 }
24449                 ]
24450             }
24451         ]
24452     }
24453 });
24454
24455  
24456
24457  /*
24458  * - LGPL
24459  *
24460  * MonthField
24461  * 
24462  */
24463
24464 /**
24465  * @class Roo.bootstrap.form.MonthField
24466  * @extends Roo.bootstrap.form.Input
24467  * Bootstrap MonthField class
24468  * 
24469  * @cfg {String} language default en
24470  * 
24471  * @constructor
24472  * Create a new MonthField
24473  * @param {Object} config The config object
24474  */
24475
24476 Roo.bootstrap.form.MonthField = function(config){
24477     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24478     
24479     this.addEvents({
24480         /**
24481          * @event show
24482          * Fires when this field show.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         show : true,
24487         /**
24488          * @event show
24489          * Fires when this field hide.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {Mixed} date The date value
24492          */
24493         hide : true,
24494         /**
24495          * @event select
24496          * Fires when select a date.
24497          * @param {Roo.bootstrap.form.MonthField} this
24498          * @param {String} oldvalue The old value
24499          * @param {String} newvalue The new value
24500          */
24501         select : true
24502     });
24503 };
24504
24505 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24506     
24507     onRender: function(ct, position)
24508     {
24509         
24510         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24511         
24512         this.language = this.language || 'en';
24513         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24514         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24515         
24516         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24517         this.isInline = false;
24518         this.isInput = true;
24519         this.component = this.el.select('.add-on', true).first() || false;
24520         this.component = (this.component && this.component.length === 0) ? false : this.component;
24521         this.hasInput = this.component && this.inputEL().length;
24522         
24523         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24524         
24525         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24526         
24527         this.picker().on('mousedown', this.onMousedown, this);
24528         this.picker().on('click', this.onClick, this);
24529         
24530         this.picker().addClass('datepicker-dropdown');
24531         
24532         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24533             v.setStyle('width', '189px');
24534         });
24535         
24536         this.fillMonths();
24537         
24538         this.update();
24539         
24540         if(this.isInline) {
24541             this.show();
24542         }
24543         
24544     },
24545     
24546     setValue: function(v, suppressEvent)
24547     {   
24548         var o = this.getValue();
24549         
24550         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24551         
24552         this.update();
24553
24554         if(suppressEvent !== true){
24555             this.fireEvent('select', this, o, v);
24556         }
24557         
24558     },
24559     
24560     getValue: function()
24561     {
24562         return this.value;
24563     },
24564     
24565     onClick: function(e) 
24566     {
24567         e.stopPropagation();
24568         e.preventDefault();
24569         
24570         var target = e.getTarget();
24571         
24572         if(target.nodeName.toLowerCase() === 'i'){
24573             target = Roo.get(target).dom.parentNode;
24574         }
24575         
24576         var nodeName = target.nodeName;
24577         var className = target.className;
24578         var html = target.innerHTML;
24579         
24580         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24581             return;
24582         }
24583         
24584         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24585         
24586         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24587         
24588         this.hide();
24589                         
24590     },
24591     
24592     picker : function()
24593     {
24594         return this.pickerEl;
24595     },
24596     
24597     fillMonths: function()
24598     {    
24599         var i = 0;
24600         var months = this.picker().select('>.datepicker-months td', true).first();
24601         
24602         months.dom.innerHTML = '';
24603         
24604         while (i < 12) {
24605             var month = {
24606                 tag: 'span',
24607                 cls: 'month',
24608                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24609             };
24610             
24611             months.createChild(month);
24612         }
24613         
24614     },
24615     
24616     update: function()
24617     {
24618         var _this = this;
24619         
24620         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24621             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24622         }
24623         
24624         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24625             e.removeClass('active');
24626             
24627             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24628                 e.addClass('active');
24629             }
24630         })
24631     },
24632     
24633     place: function()
24634     {
24635         if(this.isInline) {
24636             return;
24637         }
24638         
24639         this.picker().removeClass(['bottom', 'top']);
24640         
24641         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24642             /*
24643              * place to the top of element!
24644              *
24645              */
24646             
24647             this.picker().addClass('top');
24648             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24649             
24650             return;
24651         }
24652         
24653         this.picker().addClass('bottom');
24654         
24655         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24656     },
24657     
24658     onFocus : function()
24659     {
24660         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24661         this.show();
24662     },
24663     
24664     onBlur : function()
24665     {
24666         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24667         
24668         var d = this.inputEl().getValue();
24669         
24670         this.setValue(d);
24671                 
24672         this.hide();
24673     },
24674     
24675     show : function()
24676     {
24677         this.picker().show();
24678         this.picker().select('>.datepicker-months', true).first().show();
24679         this.update();
24680         this.place();
24681         
24682         this.fireEvent('show', this, this.date);
24683     },
24684     
24685     hide : function()
24686     {
24687         if(this.isInline) {
24688             return;
24689         }
24690         this.picker().hide();
24691         this.fireEvent('hide', this, this.date);
24692         
24693     },
24694     
24695     onMousedown: function(e)
24696     {
24697         e.stopPropagation();
24698         e.preventDefault();
24699     },
24700     
24701     keyup: function(e)
24702     {
24703         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24704         this.update();
24705     },
24706
24707     fireKey: function(e)
24708     {
24709         if (!this.picker().isVisible()){
24710             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24711                 this.show();
24712             }
24713             return;
24714         }
24715         
24716         var dir;
24717         
24718         switch(e.keyCode){
24719             case 27: // escape
24720                 this.hide();
24721                 e.preventDefault();
24722                 break;
24723             case 37: // left
24724             case 39: // right
24725                 dir = e.keyCode == 37 ? -1 : 1;
24726                 
24727                 this.vIndex = this.vIndex + dir;
24728                 
24729                 if(this.vIndex < 0){
24730                     this.vIndex = 0;
24731                 }
24732                 
24733                 if(this.vIndex > 11){
24734                     this.vIndex = 11;
24735                 }
24736                 
24737                 if(isNaN(this.vIndex)){
24738                     this.vIndex = 0;
24739                 }
24740                 
24741                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24742                 
24743                 break;
24744             case 38: // up
24745             case 40: // down
24746                 
24747                 dir = e.keyCode == 38 ? -1 : 1;
24748                 
24749                 this.vIndex = this.vIndex + dir * 4;
24750                 
24751                 if(this.vIndex < 0){
24752                     this.vIndex = 0;
24753                 }
24754                 
24755                 if(this.vIndex > 11){
24756                     this.vIndex = 11;
24757                 }
24758                 
24759                 if(isNaN(this.vIndex)){
24760                     this.vIndex = 0;
24761                 }
24762                 
24763                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24764                 break;
24765                 
24766             case 13: // enter
24767                 
24768                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24769                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24770                 }
24771                 
24772                 this.hide();
24773                 e.preventDefault();
24774                 break;
24775             case 9: // tab
24776                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24777                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24778                 }
24779                 this.hide();
24780                 break;
24781             case 16: // shift
24782             case 17: // ctrl
24783             case 18: // alt
24784                 break;
24785             default :
24786                 this.hide();
24787                 
24788         }
24789     },
24790     
24791     remove: function() 
24792     {
24793         this.picker().remove();
24794     }
24795    
24796 });
24797
24798 Roo.apply(Roo.bootstrap.form.MonthField,  {
24799     
24800     content : {
24801         tag: 'tbody',
24802         cn: [
24803         {
24804             tag: 'tr',
24805             cn: [
24806             {
24807                 tag: 'td',
24808                 colspan: '7'
24809             }
24810             ]
24811         }
24812         ]
24813     },
24814     
24815     dates:{
24816         en: {
24817             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24818             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24819         }
24820     }
24821 });
24822
24823 Roo.apply(Roo.bootstrap.form.MonthField,  {
24824   
24825     template : {
24826         tag: 'div',
24827         cls: 'datepicker dropdown-menu roo-dynamic',
24828         cn: [
24829             {
24830                 tag: 'div',
24831                 cls: 'datepicker-months',
24832                 cn: [
24833                 {
24834                     tag: 'table',
24835                     cls: 'table-condensed',
24836                     cn:[
24837                         Roo.bootstrap.form.DateField.content
24838                     ]
24839                 }
24840                 ]
24841             }
24842         ]
24843     }
24844 });
24845
24846  
24847
24848  
24849  /*
24850  * - LGPL
24851  *
24852  * CheckBox
24853  * 
24854  */
24855
24856 /**
24857  * @class Roo.bootstrap.form.CheckBox
24858  * @extends Roo.bootstrap.form.Input
24859  * Bootstrap CheckBox class
24860  * 
24861  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24862  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24863  * @cfg {String} boxLabel The text that appears beside the checkbox
24864  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24865  * @cfg {Boolean} checked initnal the element
24866  * @cfg {Boolean} inline inline the element (default false)
24867  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24868  * @cfg {String} tooltip label tooltip
24869  * 
24870  * @constructor
24871  * Create a new CheckBox
24872  * @param {Object} config The config object
24873  */
24874
24875 Roo.bootstrap.form.CheckBox = function(config){
24876     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24877    
24878     this.addEvents({
24879         /**
24880         * @event check
24881         * Fires when the element is checked or unchecked.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         * @param {Boolean} checked The new checked value
24884         */
24885        check : true,
24886        /**
24887         * @event click
24888         * Fires when the element is click.
24889         * @param {Roo.bootstrap.form.CheckBox} this This input
24890         */
24891        click : true
24892     });
24893     
24894 };
24895
24896 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24897   
24898     inputType: 'checkbox',
24899     inputValue: 1,
24900     valueOff: 0,
24901     boxLabel: false,
24902     checked: false,
24903     weight : false,
24904     inline: false,
24905     tooltip : '',
24906     
24907     // checkbox success does not make any sense really.. 
24908     invalidClass : "",
24909     validClass : "",
24910     
24911     
24912     getAutoCreate : function()
24913     {
24914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24915         
24916         var id = Roo.id();
24917         
24918         var cfg = {};
24919         
24920         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24921         
24922         if(this.inline){
24923             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24924         }
24925         
24926         var input =  {
24927             tag: 'input',
24928             id : id,
24929             type : this.inputType,
24930             value : this.inputValue,
24931             cls : 'roo-' + this.inputType, //'form-box',
24932             placeholder : this.placeholder || ''
24933             
24934         };
24935         
24936         if(this.inputType != 'radio'){
24937             var hidden =  {
24938                 tag: 'input',
24939                 type : 'hidden',
24940                 cls : 'roo-hidden-value',
24941                 value : this.checked ? this.inputValue : this.valueOff
24942             };
24943         }
24944         
24945             
24946         if (this.weight) { // Validity check?
24947             cfg.cls += " " + this.inputType + "-" + this.weight;
24948         }
24949         
24950         if (this.disabled) {
24951             input.disabled=true;
24952         }
24953         
24954         if(this.checked){
24955             input.checked = this.checked;
24956         }
24957         
24958         if (this.name) {
24959             
24960             input.name = this.name;
24961             
24962             if(this.inputType != 'radio'){
24963                 hidden.name = this.name;
24964                 input.name = '_hidden_' + this.name;
24965             }
24966         }
24967         
24968         if (this.size) {
24969             input.cls += ' input-' + this.size;
24970         }
24971         
24972         var settings=this;
24973         
24974         ['xs','sm','md','lg'].map(function(size){
24975             if (settings[size]) {
24976                 cfg.cls += ' col-' + size + '-' + settings[size];
24977             }
24978         });
24979         
24980         var inputblock = input;
24981          
24982         if (this.before || this.after) {
24983             
24984             inputblock = {
24985                 cls : 'input-group',
24986                 cn :  [] 
24987             };
24988             
24989             if (this.before) {
24990                 inputblock.cn.push({
24991                     tag :'span',
24992                     cls : 'input-group-addon',
24993                     html : this.before
24994                 });
24995             }
24996             
24997             inputblock.cn.push(input);
24998             
24999             if(this.inputType != 'radio'){
25000                 inputblock.cn.push(hidden);
25001             }
25002             
25003             if (this.after) {
25004                 inputblock.cn.push({
25005                     tag :'span',
25006                     cls : 'input-group-addon',
25007                     html : this.after
25008                 });
25009             }
25010             
25011         }
25012         var boxLabelCfg = false;
25013         
25014         if(this.boxLabel){
25015            
25016             boxLabelCfg = {
25017                 tag: 'label',
25018                 //'for': id, // box label is handled by onclick - so no for...
25019                 cls: 'box-label',
25020                 html: this.boxLabel
25021             };
25022             if(this.tooltip){
25023                 boxLabelCfg.tooltip = this.tooltip;
25024             }
25025              
25026         }
25027         
25028         
25029         if (align ==='left' && this.fieldLabel.length) {
25030 //                Roo.log("left and has label");
25031             cfg.cn = [
25032                 {
25033                     tag: 'label',
25034                     'for' :  id,
25035                     cls : 'control-label',
25036                     html : this.fieldLabel
25037                 },
25038                 {
25039                     cls : "", 
25040                     cn: [
25041                         inputblock
25042                     ]
25043                 }
25044             ];
25045             
25046             if (boxLabelCfg) {
25047                 cfg.cn[1].cn.push(boxLabelCfg);
25048             }
25049             
25050             if(this.labelWidth > 12){
25051                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25052             }
25053             
25054             if(this.labelWidth < 13 && this.labelmd == 0){
25055                 this.labelmd = this.labelWidth;
25056             }
25057             
25058             if(this.labellg > 0){
25059                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25060                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25061             }
25062             
25063             if(this.labelmd > 0){
25064                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25065                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25066             }
25067             
25068             if(this.labelsm > 0){
25069                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25070                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25071             }
25072             
25073             if(this.labelxs > 0){
25074                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25075                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25076             }
25077             
25078         } else if ( this.fieldLabel.length) {
25079 //                Roo.log(" label");
25080                 cfg.cn = [
25081                    
25082                     {
25083                         tag: this.boxLabel ? 'span' : 'label',
25084                         'for': id,
25085                         cls: 'control-label box-input-label',
25086                         //cls : 'input-group-addon',
25087                         html : this.fieldLabel
25088                     },
25089                     
25090                     inputblock
25091                     
25092                 ];
25093                 if (boxLabelCfg) {
25094                     cfg.cn.push(boxLabelCfg);
25095                 }
25096
25097         } else {
25098             
25099 //                Roo.log(" no label && no align");
25100                 cfg.cn = [  inputblock ] ;
25101                 if (boxLabelCfg) {
25102                     cfg.cn.push(boxLabelCfg);
25103                 }
25104
25105                 
25106         }
25107         
25108        
25109         
25110         if(this.inputType != 'radio'){
25111             cfg.cn.push(hidden);
25112         }
25113         
25114         return cfg;
25115         
25116     },
25117     
25118     /**
25119      * return the real input element.
25120      */
25121     inputEl: function ()
25122     {
25123         return this.el.select('input.roo-' + this.inputType,true).first();
25124     },
25125     hiddenEl: function ()
25126     {
25127         return this.el.select('input.roo-hidden-value',true).first();
25128     },
25129     
25130     labelEl: function()
25131     {
25132         return this.el.select('label.control-label',true).first();
25133     },
25134     /* depricated... */
25135     
25136     label: function()
25137     {
25138         return this.labelEl();
25139     },
25140     
25141     boxLabelEl: function()
25142     {
25143         return this.el.select('label.box-label',true).first();
25144     },
25145     
25146     initEvents : function()
25147     {
25148 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25149         
25150         this.inputEl().on('click', this.onClick,  this);
25151         
25152         if (this.boxLabel) { 
25153             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25154         }
25155         
25156         this.startValue = this.getValue();
25157         
25158         if(this.groupId){
25159             Roo.bootstrap.form.CheckBox.register(this);
25160         }
25161     },
25162     
25163     onClick : function(e)
25164     {   
25165         if(this.fireEvent('click', this, e) !== false){
25166             this.setChecked(!this.checked);
25167         }
25168         
25169     },
25170     
25171     setChecked : function(state,suppressEvent)
25172     {
25173         this.startValue = this.getValue();
25174
25175         if(this.inputType == 'radio'){
25176             
25177             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25178                 e.dom.checked = false;
25179             });
25180             
25181             this.inputEl().dom.checked = true;
25182             
25183             this.inputEl().dom.value = this.inputValue;
25184             
25185             if(suppressEvent !== true){
25186                 this.fireEvent('check', this, true);
25187             }
25188             
25189             this.validate();
25190             
25191             return;
25192         }
25193         
25194         this.checked = state;
25195         
25196         this.inputEl().dom.checked = state;
25197         
25198         
25199         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25200         
25201         if(suppressEvent !== true){
25202             this.fireEvent('check', this, state);
25203         }
25204         
25205         this.validate();
25206     },
25207     
25208     getValue : function()
25209     {
25210         if(this.inputType == 'radio'){
25211             return this.getGroupValue();
25212         }
25213         
25214         return this.hiddenEl().dom.value;
25215         
25216     },
25217     
25218     getGroupValue : function()
25219     {
25220         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25221             return '';
25222         }
25223         
25224         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25225     },
25226     
25227     setValue : function(v,suppressEvent)
25228     {
25229         if(this.inputType == 'radio'){
25230             this.setGroupValue(v, suppressEvent);
25231             return;
25232         }
25233         
25234         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25235         
25236         this.validate();
25237     },
25238     
25239     setGroupValue : function(v, suppressEvent)
25240     {
25241         this.startValue = this.getValue();
25242         
25243         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25244             e.dom.checked = false;
25245             
25246             if(e.dom.value == v){
25247                 e.dom.checked = true;
25248             }
25249         });
25250         
25251         if(suppressEvent !== true){
25252             this.fireEvent('check', this, true);
25253         }
25254
25255         this.validate();
25256         
25257         return;
25258     },
25259     
25260     validate : function()
25261     {
25262         if(this.getVisibilityEl().hasClass('hidden')){
25263             return true;
25264         }
25265         
25266         if(
25267                 this.disabled || 
25268                 (this.inputType == 'radio' && this.validateRadio()) ||
25269                 (this.inputType == 'checkbox' && this.validateCheckbox())
25270         ){
25271             this.markValid();
25272             return true;
25273         }
25274         
25275         this.markInvalid();
25276         return false;
25277     },
25278     
25279     validateRadio : function()
25280     {
25281         if(this.getVisibilityEl().hasClass('hidden')){
25282             return true;
25283         }
25284         
25285         if(this.allowBlank){
25286             return true;
25287         }
25288         
25289         var valid = false;
25290         
25291         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25292             if(!e.dom.checked){
25293                 return;
25294             }
25295             
25296             valid = true;
25297             
25298             return false;
25299         });
25300         
25301         return valid;
25302     },
25303     
25304     validateCheckbox : function()
25305     {
25306         if(!this.groupId){
25307             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25308             //return (this.getValue() == this.inputValue) ? true : false;
25309         }
25310         
25311         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25312         
25313         if(!group){
25314             return false;
25315         }
25316         
25317         var r = false;
25318         
25319         for(var i in group){
25320             if(group[i].el.isVisible(true)){
25321                 r = false;
25322                 break;
25323             }
25324             
25325             r = true;
25326         }
25327         
25328         for(var i in group){
25329             if(r){
25330                 break;
25331             }
25332             
25333             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25334         }
25335         
25336         return r;
25337     },
25338     
25339     /**
25340      * Mark this field as valid
25341      */
25342     markValid : function()
25343     {
25344         var _this = this;
25345         
25346         this.fireEvent('valid', this);
25347         
25348         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25349         
25350         if(this.groupId){
25351             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25352         }
25353         
25354         if(label){
25355             label.markValid();
25356         }
25357
25358         if(this.inputType == 'radio'){
25359             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25360                 var fg = e.findParent('.form-group', false, true);
25361                 if (Roo.bootstrap.version == 3) {
25362                     fg.removeClass([_this.invalidClass, _this.validClass]);
25363                     fg.addClass(_this.validClass);
25364                 } else {
25365                     fg.removeClass(['is-valid', 'is-invalid']);
25366                     fg.addClass('is-valid');
25367                 }
25368             });
25369             
25370             return;
25371         }
25372
25373         if(!this.groupId){
25374             var fg = this.el.findParent('.form-group', false, true);
25375             if (Roo.bootstrap.version == 3) {
25376                 fg.removeClass([this.invalidClass, this.validClass]);
25377                 fg.addClass(this.validClass);
25378             } else {
25379                 fg.removeClass(['is-valid', 'is-invalid']);
25380                 fg.addClass('is-valid');
25381             }
25382             return;
25383         }
25384         
25385         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25386         
25387         if(!group){
25388             return;
25389         }
25390         
25391         for(var i in group){
25392             var fg = group[i].el.findParent('.form-group', false, true);
25393             if (Roo.bootstrap.version == 3) {
25394                 fg.removeClass([this.invalidClass, this.validClass]);
25395                 fg.addClass(this.validClass);
25396             } else {
25397                 fg.removeClass(['is-valid', 'is-invalid']);
25398                 fg.addClass('is-valid');
25399             }
25400         }
25401     },
25402     
25403      /**
25404      * Mark this field as invalid
25405      * @param {String} msg The validation message
25406      */
25407     markInvalid : function(msg)
25408     {
25409         if(this.allowBlank){
25410             return;
25411         }
25412         
25413         var _this = this;
25414         
25415         this.fireEvent('invalid', this, msg);
25416         
25417         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25418         
25419         if(this.groupId){
25420             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25421         }
25422         
25423         if(label){
25424             label.markInvalid();
25425         }
25426             
25427         if(this.inputType == 'radio'){
25428             
25429             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25430                 var fg = e.findParent('.form-group', false, true);
25431                 if (Roo.bootstrap.version == 3) {
25432                     fg.removeClass([_this.invalidClass, _this.validClass]);
25433                     fg.addClass(_this.invalidClass);
25434                 } else {
25435                     fg.removeClass(['is-invalid', 'is-valid']);
25436                     fg.addClass('is-invalid');
25437                 }
25438             });
25439             
25440             return;
25441         }
25442         
25443         if(!this.groupId){
25444             var fg = this.el.findParent('.form-group', false, true);
25445             if (Roo.bootstrap.version == 3) {
25446                 fg.removeClass([_this.invalidClass, _this.validClass]);
25447                 fg.addClass(_this.invalidClass);
25448             } else {
25449                 fg.removeClass(['is-invalid', 'is-valid']);
25450                 fg.addClass('is-invalid');
25451             }
25452             return;
25453         }
25454         
25455         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25456         
25457         if(!group){
25458             return;
25459         }
25460         
25461         for(var i in group){
25462             var fg = group[i].el.findParent('.form-group', false, true);
25463             if (Roo.bootstrap.version == 3) {
25464                 fg.removeClass([_this.invalidClass, _this.validClass]);
25465                 fg.addClass(_this.invalidClass);
25466             } else {
25467                 fg.removeClass(['is-invalid', 'is-valid']);
25468                 fg.addClass('is-invalid');
25469             }
25470         }
25471         
25472     },
25473     
25474     clearInvalid : function()
25475     {
25476         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25477         
25478         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25479         
25480         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25481         
25482         if (label && label.iconEl) {
25483             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25484             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25485         }
25486     },
25487     
25488     disable : function()
25489     {
25490         if(this.inputType != 'radio'){
25491             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25492             return;
25493         }
25494         
25495         var _this = this;
25496         
25497         if(this.rendered){
25498             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25499                 _this.getActionEl().addClass(this.disabledClass);
25500                 e.dom.disabled = true;
25501             });
25502         }
25503         
25504         this.disabled = true;
25505         this.fireEvent("disable", this);
25506         return this;
25507     },
25508
25509     enable : function()
25510     {
25511         if(this.inputType != 'radio'){
25512             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25513             return;
25514         }
25515         
25516         var _this = this;
25517         
25518         if(this.rendered){
25519             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25520                 _this.getActionEl().removeClass(this.disabledClass);
25521                 e.dom.disabled = false;
25522             });
25523         }
25524         
25525         this.disabled = false;
25526         this.fireEvent("enable", this);
25527         return this;
25528     },
25529     
25530     setBoxLabel : function(v)
25531     {
25532         this.boxLabel = v;
25533         
25534         if(this.rendered){
25535             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25536         }
25537     }
25538
25539 });
25540
25541 Roo.apply(Roo.bootstrap.form.CheckBox, {
25542     
25543     groups: {},
25544     
25545      /**
25546     * register a CheckBox Group
25547     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25548     */
25549     register : function(checkbox)
25550     {
25551         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25552             this.groups[checkbox.groupId] = {};
25553         }
25554         
25555         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25556             return;
25557         }
25558         
25559         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25560         
25561     },
25562     /**
25563     * fetch a CheckBox Group based on the group ID
25564     * @param {string} the group ID
25565     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25566     */
25567     get: function(groupId) {
25568         if (typeof(this.groups[groupId]) == 'undefined') {
25569             return false;
25570         }
25571         
25572         return this.groups[groupId] ;
25573     }
25574     
25575     
25576 });
25577 /*
25578  * - LGPL
25579  *
25580  * RadioItem
25581  * 
25582  */
25583
25584 /**
25585  * @class Roo.bootstrap.form.Radio
25586  * @extends Roo.bootstrap.Component
25587  * Bootstrap Radio class
25588  * @cfg {String} boxLabel - the label associated
25589  * @cfg {String} value - the value of radio
25590  * 
25591  * @constructor
25592  * Create a new Radio
25593  * @param {Object} config The config object
25594  */
25595 Roo.bootstrap.form.Radio = function(config){
25596     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25597     
25598 };
25599
25600 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25601     
25602     boxLabel : '',
25603     
25604     value : '',
25605     
25606     getAutoCreate : function()
25607     {
25608         var cfg = {
25609             tag : 'div',
25610             cls : 'form-group radio',
25611             cn : [
25612                 {
25613                     tag : 'label',
25614                     cls : 'box-label',
25615                     html : this.boxLabel
25616                 }
25617             ]
25618         };
25619         
25620         return cfg;
25621     },
25622     
25623     initEvents : function() 
25624     {
25625         this.parent().register(this);
25626         
25627         this.el.on('click', this.onClick, this);
25628         
25629     },
25630     
25631     onClick : function(e)
25632     {
25633         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25634             this.setChecked(true);
25635         }
25636     },
25637     
25638     setChecked : function(state, suppressEvent)
25639     {
25640         this.parent().setValue(this.value, suppressEvent);
25641         
25642     },
25643     
25644     setBoxLabel : function(v)
25645     {
25646         this.boxLabel = v;
25647         
25648         if(this.rendered){
25649             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25650         }
25651     }
25652     
25653 });
25654  
25655
25656  /*
25657  * - LGPL
25658  *
25659  * Input
25660  * 
25661  */
25662
25663 /**
25664  * @class Roo.bootstrap.form.SecurePass
25665  * @extends Roo.bootstrap.form.Input
25666  * Bootstrap SecurePass class
25667  *
25668  * 
25669  * @constructor
25670  * Create a new SecurePass
25671  * @param {Object} config The config object
25672  */
25673  
25674 Roo.bootstrap.form.SecurePass = function (config) {
25675     // these go here, so the translation tool can replace them..
25676     this.errors = {
25677         PwdEmpty: "Please type a password, and then retype it to confirm.",
25678         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25679         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25680         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25681         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25682         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25683         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25684         TooWeak: "Your password is Too Weak."
25685     },
25686     this.meterLabel = "Password strength:";
25687     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25688     this.meterClass = [
25689         "roo-password-meter-tooweak", 
25690         "roo-password-meter-weak", 
25691         "roo-password-meter-medium", 
25692         "roo-password-meter-strong", 
25693         "roo-password-meter-grey"
25694     ];
25695     
25696     this.errors = {};
25697     
25698     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25699 }
25700
25701 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25702     /**
25703      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25704      * {
25705      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25706      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25707      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25708      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25709      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25710      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25711      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25712      * })
25713      */
25714     // private
25715     
25716     meterWidth: 300,
25717     errorMsg :'',    
25718     errors: false,
25719     imageRoot: '/',
25720     /**
25721      * @cfg {String/Object} Label for the strength meter (defaults to
25722      * 'Password strength:')
25723      */
25724     // private
25725     meterLabel: '',
25726     /**
25727      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25728      * ['Weak', 'Medium', 'Strong'])
25729      */
25730     // private    
25731     pwdStrengths: false,    
25732     // private
25733     strength: 0,
25734     // private
25735     _lastPwd: null,
25736     // private
25737     kCapitalLetter: 0,
25738     kSmallLetter: 1,
25739     kDigit: 2,
25740     kPunctuation: 3,
25741     
25742     insecure: false,
25743     // private
25744     initEvents: function ()
25745     {
25746         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25747
25748         if (this.el.is('input[type=password]') && Roo.isSafari) {
25749             this.el.on('keydown', this.SafariOnKeyDown, this);
25750         }
25751
25752         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25753     },
25754     // private
25755     onRender: function (ct, position)
25756     {
25757         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25758         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25759         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25760
25761         this.trigger.createChild({
25762                    cn: [
25763                     {
25764                     //id: 'PwdMeter',
25765                     tag: 'div',
25766                     cls: 'roo-password-meter-grey col-xs-12',
25767                     style: {
25768                         //width: 0,
25769                         //width: this.meterWidth + 'px'                                                
25770                         }
25771                     },
25772                     {                            
25773                          cls: 'roo-password-meter-text'                          
25774                     }
25775                 ]            
25776         });
25777
25778          
25779         if (this.hideTrigger) {
25780             this.trigger.setDisplayed(false);
25781         }
25782         this.setSize(this.width || '', this.height || '');
25783     },
25784     // private
25785     onDestroy: function ()
25786     {
25787         if (this.trigger) {
25788             this.trigger.removeAllListeners();
25789             this.trigger.remove();
25790         }
25791         if (this.wrap) {
25792             this.wrap.remove();
25793         }
25794         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25795     },
25796     // private
25797     checkStrength: function ()
25798     {
25799         var pwd = this.inputEl().getValue();
25800         if (pwd == this._lastPwd) {
25801             return;
25802         }
25803
25804         var strength;
25805         if (this.ClientSideStrongPassword(pwd)) {
25806             strength = 3;
25807         } else if (this.ClientSideMediumPassword(pwd)) {
25808             strength = 2;
25809         } else if (this.ClientSideWeakPassword(pwd)) {
25810             strength = 1;
25811         } else {
25812             strength = 0;
25813         }
25814         
25815         Roo.log('strength1: ' + strength);
25816         
25817         //var pm = this.trigger.child('div/div/div').dom;
25818         var pm = this.trigger.child('div/div');
25819         pm.removeClass(this.meterClass);
25820         pm.addClass(this.meterClass[strength]);
25821                 
25822         
25823         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25824                 
25825         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25826         
25827         this._lastPwd = pwd;
25828     },
25829     reset: function ()
25830     {
25831         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25832         
25833         this._lastPwd = '';
25834         
25835         var pm = this.trigger.child('div/div');
25836         pm.removeClass(this.meterClass);
25837         pm.addClass('roo-password-meter-grey');        
25838         
25839         
25840         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25841         
25842         pt.innerHTML = '';
25843         this.inputEl().dom.type='password';
25844     },
25845     // private
25846     validateValue: function (value)
25847     {
25848         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25849             return false;
25850         }
25851         if (value.length == 0) {
25852             if (this.allowBlank) {
25853                 this.clearInvalid();
25854                 return true;
25855             }
25856
25857             this.markInvalid(this.errors.PwdEmpty);
25858             this.errorMsg = this.errors.PwdEmpty;
25859             return false;
25860         }
25861         
25862         if(this.insecure){
25863             return true;
25864         }
25865         
25866         if (!value.match(/[\x21-\x7e]+/)) {
25867             this.markInvalid(this.errors.PwdBadChar);
25868             this.errorMsg = this.errors.PwdBadChar;
25869             return false;
25870         }
25871         if (value.length < 6) {
25872             this.markInvalid(this.errors.PwdShort);
25873             this.errorMsg = this.errors.PwdShort;
25874             return false;
25875         }
25876         if (value.length > 16) {
25877             this.markInvalid(this.errors.PwdLong);
25878             this.errorMsg = this.errors.PwdLong;
25879             return false;
25880         }
25881         var strength;
25882         if (this.ClientSideStrongPassword(value)) {
25883             strength = 3;
25884         } else if (this.ClientSideMediumPassword(value)) {
25885             strength = 2;
25886         } else if (this.ClientSideWeakPassword(value)) {
25887             strength = 1;
25888         } else {
25889             strength = 0;
25890         }
25891
25892         
25893         if (strength < 2) {
25894             //this.markInvalid(this.errors.TooWeak);
25895             this.errorMsg = this.errors.TooWeak;
25896             //return false;
25897         }
25898         
25899         
25900         console.log('strength2: ' + strength);
25901         
25902         //var pm = this.trigger.child('div/div/div').dom;
25903         
25904         var pm = this.trigger.child('div/div');
25905         pm.removeClass(this.meterClass);
25906         pm.addClass(this.meterClass[strength]);
25907                 
25908         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25909                 
25910         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25911         
25912         this.errorMsg = ''; 
25913         return true;
25914     },
25915     // private
25916     CharacterSetChecks: function (type)
25917     {
25918         this.type = type;
25919         this.fResult = false;
25920     },
25921     // private
25922     isctype: function (character, type)
25923     {
25924         switch (type) {  
25925             case this.kCapitalLetter:
25926                 if (character >= 'A' && character <= 'Z') {
25927                     return true;
25928                 }
25929                 break;
25930             
25931             case this.kSmallLetter:
25932                 if (character >= 'a' && character <= 'z') {
25933                     return true;
25934                 }
25935                 break;
25936             
25937             case this.kDigit:
25938                 if (character >= '0' && character <= '9') {
25939                     return true;
25940                 }
25941                 break;
25942             
25943             case this.kPunctuation:
25944                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25945                     return true;
25946                 }
25947                 break;
25948             
25949             default:
25950                 return false;
25951         }
25952
25953     },
25954     // private
25955     IsLongEnough: function (pwd, size)
25956     {
25957         return !(pwd == null || isNaN(size) || pwd.length < size);
25958     },
25959     // private
25960     SpansEnoughCharacterSets: function (word, nb)
25961     {
25962         if (!this.IsLongEnough(word, nb))
25963         {
25964             return false;
25965         }
25966
25967         var characterSetChecks = new Array(
25968             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25969             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25970         );
25971         
25972         for (var index = 0; index < word.length; ++index) {
25973             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25974                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25975                     characterSetChecks[nCharSet].fResult = true;
25976                     break;
25977                 }
25978             }
25979         }
25980
25981         var nCharSets = 0;
25982         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25983             if (characterSetChecks[nCharSet].fResult) {
25984                 ++nCharSets;
25985             }
25986         }
25987
25988         if (nCharSets < nb) {
25989             return false;
25990         }
25991         return true;
25992     },
25993     // private
25994     ClientSideStrongPassword: function (pwd)
25995     {
25996         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25997     },
25998     // private
25999     ClientSideMediumPassword: function (pwd)
26000     {
26001         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26002     },
26003     // private
26004     ClientSideWeakPassword: function (pwd)
26005     {
26006         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26007     }
26008           
26009 });
26010 Roo.htmleditor = {};
26011  
26012 /**
26013  * @class Roo.htmleditor.Filter
26014  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26015  * @cfg {DomElement} node The node to iterate and filter
26016  * @cfg {boolean|String|Array} tag Tags to replace 
26017  * @constructor
26018  * Create a new Filter.
26019  * @param {Object} config Configuration options
26020  */
26021
26022
26023
26024 Roo.htmleditor.Filter = function(cfg) {
26025     Roo.apply(this.cfg);
26026     // this does not actually call walk as it's really just a abstract class
26027 }
26028
26029
26030 Roo.htmleditor.Filter.prototype = {
26031     
26032     node: false,
26033     
26034     tag: false,
26035
26036     // overrride to do replace comments.
26037     replaceComment : false,
26038     
26039     // overrride to do replace or do stuff with tags..
26040     replaceTag : false,
26041     
26042     walk : function(dom)
26043     {
26044         Roo.each( Array.from(dom.childNodes), function( e ) {
26045             switch(true) {
26046                 
26047                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26048                     this.replaceComment(e);
26049                     return;
26050                 
26051                 case e.nodeType != 1: //not a node.
26052                     return;
26053                 
26054                 case this.tag === true: // everything
26055                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26056                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26057                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26058                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26059                     if (this.replaceTag && false === this.replaceTag(e)) {
26060                         return;
26061                     }
26062                     if (e.hasChildNodes()) {
26063                         this.walk(e);
26064                     }
26065                     return;
26066                 
26067                 default:    // tags .. that do not match.
26068                     if (e.hasChildNodes()) {
26069                         this.walk(e);
26070                     }
26071             }
26072             
26073         }, this);
26074         
26075     },
26076     
26077     
26078     removeNodeKeepChildren : function( node)
26079     {
26080     
26081         ar = Array.from(node.childNodes);
26082         for (var i = 0; i < ar.length; i++) {
26083          
26084             node.removeChild(ar[i]);
26085             // what if we need to walk these???
26086             node.parentNode.insertBefore(ar[i], node);
26087            
26088         }
26089         node.parentNode.removeChild(node);
26090     }
26091 }; 
26092
26093 /**
26094  * @class Roo.htmleditor.FilterAttributes
26095  * clean attributes and  styles including http:// etc.. in attribute
26096  * @constructor
26097 * Run a new Attribute Filter
26098 * @param {Object} config Configuration options
26099  */
26100 Roo.htmleditor.FilterAttributes = function(cfg)
26101 {
26102     Roo.apply(this, cfg);
26103     this.attrib_black = this.attrib_black || [];
26104     this.attrib_white = this.attrib_white || [];
26105
26106     this.attrib_clean = this.attrib_clean || [];
26107     this.style_white = this.style_white || [];
26108     this.style_black = this.style_black || [];
26109     this.walk(cfg.node);
26110 }
26111
26112 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26113 {
26114     tag: true, // all tags
26115     
26116     attrib_black : false, // array
26117     attrib_clean : false,
26118     attrib_white : false,
26119
26120     style_white : false,
26121     style_black : false,
26122      
26123      
26124     replaceTag : function(node)
26125     {
26126         if (!node.attributes || !node.attributes.length) {
26127             return true;
26128         }
26129         
26130         for (var i = node.attributes.length-1; i > -1 ; i--) {
26131             var a = node.attributes[i];
26132             //console.log(a);
26133             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26134                 node.removeAttribute(a.name);
26135                 continue;
26136             }
26137             
26138             
26139             
26140             if (a.name.toLowerCase().substr(0,2)=='on')  {
26141                 node.removeAttribute(a.name);
26142                 continue;
26143             }
26144             
26145             
26146             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26147                 node.removeAttribute(a.name);
26148                 continue;
26149             }
26150             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26151                 this.cleanAttr(node,a.name,a.value); // fixme..
26152                 continue;
26153             }
26154             if (a.name == 'style') {
26155                 this.cleanStyle(node,a.name,a.value);
26156                 continue;
26157             }
26158             /// clean up MS crap..
26159             // tecnically this should be a list of valid class'es..
26160             
26161             
26162             if (a.name == 'class') {
26163                 if (a.value.match(/^Mso/)) {
26164                     node.removeAttribute('class');
26165                 }
26166                 
26167                 if (a.value.match(/^body$/)) {
26168                     node.removeAttribute('class');
26169                 }
26170                 continue;
26171             }
26172             
26173             
26174             // style cleanup!?
26175             // class cleanup?
26176             
26177         }
26178         return true; // clean children
26179     },
26180         
26181     cleanAttr: function(node, n,v)
26182     {
26183         
26184         if (v.match(/^\./) || v.match(/^\//)) {
26185             return;
26186         }
26187         if (v.match(/^(http|https):\/\//)
26188             || v.match(/^mailto:/) 
26189             || v.match(/^ftp:/)
26190             || v.match(/^data:/)
26191             ) {
26192             return;
26193         }
26194         if (v.match(/^#/)) {
26195             return;
26196         }
26197         if (v.match(/^\{/)) { // allow template editing.
26198             return;
26199         }
26200 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26201         node.removeAttribute(n);
26202         
26203     },
26204     cleanStyle : function(node,  n,v)
26205     {
26206         if (v.match(/expression/)) { //XSS?? should we even bother..
26207             node.removeAttribute(n);
26208             return;
26209         }
26210         
26211         var parts = v.split(/;/);
26212         var clean = [];
26213         
26214         Roo.each(parts, function(p) {
26215             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26216             if (!p.length) {
26217                 return true;
26218             }
26219             var l = p.split(':').shift().replace(/\s+/g,'');
26220             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26221             
26222             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26223                 return true;
26224             }
26225             //Roo.log()
26226             // only allow 'c whitelisted system attributes'
26227             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26228                 return true;
26229             }
26230             
26231             
26232             clean.push(p);
26233             return true;
26234         },this);
26235         if (clean.length) { 
26236             node.setAttribute(n, clean.join(';'));
26237         } else {
26238             node.removeAttribute(n);
26239         }
26240         
26241     }
26242         
26243         
26244         
26245     
26246 });/**
26247  * @class Roo.htmleditor.FilterBlack
26248  * remove blacklisted elements.
26249  * @constructor
26250  * Run a new Blacklisted Filter
26251  * @param {Object} config Configuration options
26252  */
26253
26254 Roo.htmleditor.FilterBlack = function(cfg)
26255 {
26256     Roo.apply(this, cfg);
26257     this.walk(cfg.node);
26258 }
26259
26260 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26261 {
26262     tag : true, // all elements.
26263    
26264     replaceTag : function(n)
26265     {
26266         n.parentNode.removeChild(n);
26267     }
26268 });
26269 /**
26270  * @class Roo.htmleditor.FilterComment
26271  * remove comments.
26272  * @constructor
26273 * Run a new Comments Filter
26274 * @param {Object} config Configuration options
26275  */
26276 Roo.htmleditor.FilterComment = function(cfg)
26277 {
26278     this.walk(cfg.node);
26279 }
26280
26281 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26282 {
26283   
26284     replaceComment : function(n)
26285     {
26286         n.parentNode.removeChild(n);
26287     }
26288 });/**
26289  * @class Roo.htmleditor.FilterKeepChildren
26290  * remove tags but keep children
26291  * @constructor
26292  * Run a new Keep Children Filter
26293  * @param {Object} config Configuration options
26294  */
26295
26296 Roo.htmleditor.FilterKeepChildren = function(cfg)
26297 {
26298     Roo.apply(this, cfg);
26299     if (this.tag === false) {
26300         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26301     }
26302     // hacky?
26303     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26304         this.cleanNamespace = true;
26305     }
26306         
26307     this.walk(cfg.node);
26308 }
26309
26310 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26311 {
26312     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26313   
26314     replaceTag : function(node)
26315     {
26316         // walk children...
26317         //Roo.log(node.tagName);
26318         var ar = Array.from(node.childNodes);
26319         //remove first..
26320         
26321         for (var i = 0; i < ar.length; i++) {
26322             var e = ar[i];
26323             if (e.nodeType == 1) {
26324                 if (
26325                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26326                     || // array and it matches
26327                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26328                     ||
26329                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26330                     ||
26331                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26332                 ) {
26333                     this.replaceTag(ar[i]); // child is blacklisted as well...
26334                     continue;
26335                 }
26336             }
26337         }  
26338         ar = Array.from(node.childNodes);
26339         for (var i = 0; i < ar.length; i++) {
26340          
26341             node.removeChild(ar[i]);
26342             // what if we need to walk these???
26343             node.parentNode.insertBefore(ar[i], node);
26344             if (this.tag !== false) {
26345                 this.walk(ar[i]);
26346                 
26347             }
26348         }
26349         //Roo.log("REMOVE:" + node.tagName);
26350         node.parentNode.removeChild(node);
26351         return false; // don't walk children
26352         
26353         
26354     }
26355 });/**
26356  * @class Roo.htmleditor.FilterParagraph
26357  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26358  * like on 'push' to remove the <p> tags and replace them with line breaks.
26359  * @constructor
26360  * Run a new Paragraph Filter
26361  * @param {Object} config Configuration options
26362  */
26363
26364 Roo.htmleditor.FilterParagraph = function(cfg)
26365 {
26366     // no need to apply config.
26367     this.walk(cfg.node);
26368 }
26369
26370 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26371 {
26372     
26373      
26374     tag : 'P',
26375     
26376      
26377     replaceTag : function(node)
26378     {
26379         
26380         if (node.childNodes.length == 1 &&
26381             node.childNodes[0].nodeType == 3 &&
26382             node.childNodes[0].textContent.trim().length < 1
26383             ) {
26384             // remove and replace with '<BR>';
26385             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26386             return false; // no need to walk..
26387         }
26388         var ar = Array.from(node.childNodes);
26389         for (var i = 0; i < ar.length; i++) {
26390             node.removeChild(ar[i]);
26391             // what if we need to walk these???
26392             node.parentNode.insertBefore(ar[i], node);
26393         }
26394         // now what about this?
26395         // <p> &nbsp; </p>
26396         
26397         // double BR.
26398         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26399         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26400         node.parentNode.removeChild(node);
26401         
26402         return false;
26403
26404     }
26405     
26406 });/**
26407  * @class Roo.htmleditor.FilterSpan
26408  * filter span's with no attributes out..
26409  * @constructor
26410  * Run a new Span Filter
26411  * @param {Object} config Configuration options
26412  */
26413
26414 Roo.htmleditor.FilterSpan = function(cfg)
26415 {
26416     // no need to apply config.
26417     this.walk(cfg.node);
26418 }
26419
26420 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26421 {
26422      
26423     tag : 'SPAN',
26424      
26425  
26426     replaceTag : function(node)
26427     {
26428         if (node.attributes && node.attributes.length > 0) {
26429             return true; // walk if there are any.
26430         }
26431         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26432         return false;
26433      
26434     }
26435     
26436 });/**
26437  * @class Roo.htmleditor.FilterTableWidth
26438   try and remove table width data - as that frequently messes up other stuff.
26439  * 
26440  *      was cleanTableWidths.
26441  *
26442  * Quite often pasting from word etc.. results in tables with column and widths.
26443  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26444  *
26445  * @constructor
26446  * Run a new Table Filter
26447  * @param {Object} config Configuration options
26448  */
26449
26450 Roo.htmleditor.FilterTableWidth = function(cfg)
26451 {
26452     // no need to apply config.
26453     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26454     this.walk(cfg.node);
26455 }
26456
26457 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26458 {
26459      
26460      
26461     
26462     replaceTag: function(node) {
26463         
26464         
26465       
26466         if (node.hasAttribute('width')) {
26467             node.removeAttribute('width');
26468         }
26469         
26470          
26471         if (node.hasAttribute("style")) {
26472             // pretty basic...
26473             
26474             var styles = node.getAttribute("style").split(";");
26475             var nstyle = [];
26476             Roo.each(styles, function(s) {
26477                 if (!s.match(/:/)) {
26478                     return;
26479                 }
26480                 var kv = s.split(":");
26481                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26482                     return;
26483                 }
26484                 // what ever is left... we allow.
26485                 nstyle.push(s);
26486             });
26487             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26488             if (!nstyle.length) {
26489                 node.removeAttribute('style');
26490             }
26491         }
26492         
26493         return true; // continue doing children..
26494     }
26495 });/**
26496  * @class Roo.htmleditor.FilterWord
26497  * try and clean up all the mess that Word generates.
26498  * 
26499  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26500  
26501  * @constructor
26502  * Run a new Span Filter
26503  * @param {Object} config Configuration options
26504  */
26505
26506 Roo.htmleditor.FilterWord = function(cfg)
26507 {
26508     // no need to apply config.
26509     this.replaceDocBullets(cfg.node);
26510     
26511     this.replaceAname(cfg.node);
26512     // this is disabled as the removal is done by other filters;
26513    // this.walk(cfg.node);
26514     
26515     
26516 }
26517
26518 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26519 {
26520     tag: true,
26521      
26522     
26523     /**
26524      * Clean up MS wordisms...
26525      */
26526     replaceTag : function(node)
26527     {
26528          
26529         // no idea what this does - span with text, replaceds with just text.
26530         if(
26531                 node.nodeName == 'SPAN' &&
26532                 !node.hasAttributes() &&
26533                 node.childNodes.length == 1 &&
26534                 node.firstChild.nodeName == "#text"  
26535         ) {
26536             var textNode = node.firstChild;
26537             node.removeChild(textNode);
26538             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26539                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26540             }
26541             node.parentNode.insertBefore(textNode, node);
26542             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26543                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26544             }
26545             
26546             node.parentNode.removeChild(node);
26547             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26548         }
26549         
26550    
26551         
26552         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26553             node.parentNode.removeChild(node);
26554             return false; // dont do chidlren
26555         }
26556         //Roo.log(node.tagName);
26557         // remove - but keep children..
26558         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26559             //Roo.log('-- removed');
26560             while (node.childNodes.length) {
26561                 var cn = node.childNodes[0];
26562                 node.removeChild(cn);
26563                 node.parentNode.insertBefore(cn, node);
26564                 // move node to parent - and clean it..
26565                 if (cn.nodeType == 1) {
26566                     this.replaceTag(cn);
26567                 }
26568                 
26569             }
26570             node.parentNode.removeChild(node);
26571             /// no need to iterate chidlren = it's got none..
26572             //this.iterateChildren(node, this.cleanWord);
26573             return false; // no need to iterate children.
26574         }
26575         // clean styles
26576         if (node.className.length) {
26577             
26578             var cn = node.className.split(/\W+/);
26579             var cna = [];
26580             Roo.each(cn, function(cls) {
26581                 if (cls.match(/Mso[a-zA-Z]+/)) {
26582                     return;
26583                 }
26584                 cna.push(cls);
26585             });
26586             node.className = cna.length ? cna.join(' ') : '';
26587             if (!cna.length) {
26588                 node.removeAttribute("class");
26589             }
26590         }
26591         
26592         if (node.hasAttribute("lang")) {
26593             node.removeAttribute("lang");
26594         }
26595         
26596         if (node.hasAttribute("style")) {
26597             
26598             var styles = node.getAttribute("style").split(";");
26599             var nstyle = [];
26600             Roo.each(styles, function(s) {
26601                 if (!s.match(/:/)) {
26602                     return;
26603                 }
26604                 var kv = s.split(":");
26605                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26606                     return;
26607                 }
26608                 // what ever is left... we allow.
26609                 nstyle.push(s);
26610             });
26611             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26612             if (!nstyle.length) {
26613                 node.removeAttribute('style');
26614             }
26615         }
26616         return true; // do children
26617         
26618         
26619         
26620     },
26621     
26622     styleToObject: function(node)
26623     {
26624         var styles = (node.getAttribute("style") || '').split(";");
26625         var ret = {};
26626         Roo.each(styles, function(s) {
26627             if (!s.match(/:/)) {
26628                 return;
26629             }
26630             var kv = s.split(":");
26631              
26632             // what ever is left... we allow.
26633             ret[kv[0].trim()] = kv[1];
26634         });
26635         return ret;
26636     },
26637     
26638     
26639     replaceAname : function (doc)
26640     {
26641         // replace all the a/name without..
26642         var aa = Array.from(doc.getElementsByTagName('a'));
26643         for (var i = 0; i  < aa.length; i++) {
26644             var a = aa[i];
26645             if (a.hasAttribute("name")) {
26646                 a.removeAttribute("name");
26647             }
26648             if (a.hasAttribute("href")) {
26649                 continue;
26650             }
26651             // reparent children.
26652             this.removeNodeKeepChildren(a);
26653             
26654         }
26655         
26656         
26657         
26658     },
26659
26660     
26661     
26662     replaceDocBullets : function(doc)
26663     {
26664         // this is a bit odd - but it appears some indents use ql-indent-1
26665          //Roo.log(doc.innerHTML);
26666         
26667         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26668         for( var i = 0; i < listpara.length; i ++) {
26669             listpara[i].className = "MsoListParagraph";
26670         }
26671         
26672         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26673         for( var i = 0; i < listpara.length; i ++) {
26674             listpara[i].className = "MsoListParagraph";
26675         }
26676         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26677         for( var i = 0; i < listpara.length; i ++) {
26678             listpara[i].className = "MsoListParagraph";
26679         }
26680         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26681         for( var i = 0; i < listpara.length; i ++) {
26682             listpara[i].className = "MsoListParagraph";
26683         }
26684         
26685         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26686         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26687         for( var i = 0; i < htwo.length; i ++) {
26688             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26689                 htwo[i].className = "MsoListParagraph";
26690             }
26691         }
26692         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26693         for( var i = 0; i < listpara.length; i ++) {
26694             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26695                 listpara[i].className = "MsoListParagraph";
26696             } else {
26697                 listpara[i].className = "MsoNormalx";
26698             }
26699         }
26700        
26701         listpara = doc.getElementsByClassName('MsoListParagraph');
26702         // Roo.log(doc.innerHTML);
26703         
26704         
26705         
26706         while(listpara.length) {
26707             
26708             this.replaceDocBullet(listpara.item(0));
26709         }
26710       
26711     },
26712     
26713      
26714     
26715     replaceDocBullet : function(p)
26716     {
26717         // gather all the siblings.
26718         var ns = p,
26719             parent = p.parentNode,
26720             doc = parent.ownerDocument,
26721             items = [];
26722             
26723         var listtype = 'ul';   
26724         while (ns) {
26725             if (ns.nodeType != 1) {
26726                 ns = ns.nextSibling;
26727                 continue;
26728             }
26729             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26730                 break;
26731             }
26732             var spans = ns.getElementsByTagName('span');
26733             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26734                 items.push(ns);
26735                 ns = ns.nextSibling;
26736                 has_list = true;
26737                 if (spans.length && spans[0].hasAttribute('style')) {
26738                     var  style = this.styleToObject(spans[0]);
26739                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26740                         listtype = 'ol';
26741                     }
26742                 }
26743                 
26744                 continue;
26745             }
26746             var spans = ns.getElementsByTagName('span');
26747             if (!spans.length) {
26748                 break;
26749             }
26750             var has_list  = false;
26751             for(var i = 0; i < spans.length; i++) {
26752                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26753                     has_list = true;
26754                     break;
26755                 }
26756             }
26757             if (!has_list) {
26758                 break;
26759             }
26760             items.push(ns);
26761             ns = ns.nextSibling;
26762             
26763             
26764         }
26765         if (!items.length) {
26766             ns.className = "";
26767             return;
26768         }
26769         
26770         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26771         parent.insertBefore(ul, p);
26772         var lvl = 0;
26773         var stack = [ ul ];
26774         var last_li = false;
26775         
26776         var margin_to_depth = {};
26777         max_margins = -1;
26778         
26779         items.forEach(function(n, ipos) {
26780             //Roo.log("got innertHMLT=" + n.innerHTML);
26781             
26782             var spans = n.getElementsByTagName('span');
26783             if (!spans.length) {
26784                 //Roo.log("No spans found");
26785                  
26786                 parent.removeChild(n);
26787                 
26788                 
26789                 return; // skip it...
26790             }
26791            
26792                 
26793             var num = 1;
26794             var style = {};
26795             for(var i = 0; i < spans.length; i++) {
26796             
26797                 style = this.styleToObject(spans[i]);
26798                 if (typeof(style['mso-list']) == 'undefined') {
26799                     continue;
26800                 }
26801                 if (listtype == 'ol') {
26802                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26803                 }
26804                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26805                 break;
26806             }
26807             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26808             style = this.styleToObject(n); // mo-list is from the parent node.
26809             if (typeof(style['mso-list']) == 'undefined') {
26810                 //Roo.log("parent is missing level");
26811                   
26812                 parent.removeChild(n);
26813                  
26814                 return;
26815             }
26816             
26817             var margin = style['margin-left'];
26818             if (typeof(margin_to_depth[margin]) == 'undefined') {
26819                 max_margins++;
26820                 margin_to_depth[margin] = max_margins;
26821             }
26822             nlvl = margin_to_depth[margin] ;
26823              
26824             if (nlvl > lvl) {
26825                 //new indent
26826                 var nul = doc.createElement(listtype); // what about number lists...
26827                 if (!last_li) {
26828                     last_li = doc.createElement('li');
26829                     stack[lvl].appendChild(last_li);
26830                 }
26831                 last_li.appendChild(nul);
26832                 stack[nlvl] = nul;
26833                 
26834             }
26835             lvl = nlvl;
26836             
26837             // not starting at 1..
26838             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26839                 stack[nlvl].setAttribute("start", num);
26840             }
26841             
26842             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26843             last_li = nli;
26844             nli.innerHTML = n.innerHTML;
26845             //Roo.log("innerHTML = " + n.innerHTML);
26846             parent.removeChild(n);
26847             
26848              
26849              
26850             
26851         },this);
26852         
26853         
26854         
26855         
26856     }
26857     
26858     
26859     
26860 });
26861 /**
26862  * @class Roo.htmleditor.FilterStyleToTag
26863  * part of the word stuff... - certain 'styles' should be converted to tags.
26864  * eg.
26865  *   font-weight: bold -> bold
26866  *   ?? super / subscrit etc..
26867  * 
26868  * @constructor
26869 * Run a new style to tag filter.
26870 * @param {Object} config Configuration options
26871  */
26872 Roo.htmleditor.FilterStyleToTag = function(cfg)
26873 {
26874     
26875     this.tags = {
26876         B  : [ 'fontWeight' , 'bold'],
26877         I :  [ 'fontStyle' , 'italic'],
26878         //pre :  [ 'font-style' , 'italic'],
26879         // h1.. h6 ?? font-size?
26880         SUP : [ 'verticalAlign' , 'super' ],
26881         SUB : [ 'verticalAlign' , 'sub' ]
26882         
26883         
26884     };
26885     
26886     Roo.apply(this, cfg);
26887      
26888     
26889     this.walk(cfg.node);
26890     
26891     
26892     
26893 }
26894
26895
26896 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26897 {
26898     tag: true, // all tags
26899     
26900     tags : false,
26901     
26902     
26903     replaceTag : function(node)
26904     {
26905         
26906         
26907         if (node.getAttribute("style") === null) {
26908             return true;
26909         }
26910         var inject = [];
26911         for (var k in this.tags) {
26912             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26913                 inject.push(k);
26914                 node.style.removeProperty(this.tags[k][0]);
26915             }
26916         }
26917         if (!inject.length) {
26918             return true; 
26919         }
26920         var cn = Array.from(node.childNodes);
26921         var nn = node;
26922         Roo.each(inject, function(t) {
26923             var nc = node.ownerDocument.createElement(t);
26924             nn.appendChild(nc);
26925             nn = nc;
26926         });
26927         for(var i = 0;i < cn.length;cn++) {
26928             node.removeChild(cn[i]);
26929             nn.appendChild(cn[i]);
26930         }
26931         return true /// iterate thru
26932     }
26933     
26934 })/**
26935  * @class Roo.htmleditor.FilterLongBr
26936  * BR/BR/BR - keep a maximum of 2...
26937  * @constructor
26938  * Run a new Long BR Filter
26939  * @param {Object} config Configuration options
26940  */
26941
26942 Roo.htmleditor.FilterLongBr = function(cfg)
26943 {
26944     // no need to apply config.
26945     this.walk(cfg.node);
26946 }
26947
26948 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26949 {
26950     
26951      
26952     tag : 'BR',
26953     
26954      
26955     replaceTag : function(node)
26956     {
26957         
26958         var ps = node.nextSibling;
26959         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26960             ps = ps.nextSibling;
26961         }
26962         
26963         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26964             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26965             return false;
26966         }
26967         
26968         if (!ps || ps.nodeType != 1) {
26969             return false;
26970         }
26971         
26972         if (!ps || ps.tagName != 'BR') {
26973            
26974             return false;
26975         }
26976         
26977         
26978         
26979         
26980         
26981         if (!node.previousSibling) {
26982             return false;
26983         }
26984         var ps = node.previousSibling;
26985         
26986         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26987             ps = ps.previousSibling;
26988         }
26989         if (!ps || ps.nodeType != 1) {
26990             return false;
26991         }
26992         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26993         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26994             return false;
26995         }
26996         
26997         node.parentNode.removeChild(node); // remove me...
26998         
26999         return false; // no need to do children
27000
27001     }
27002     
27003 }); 
27004
27005 /**
27006  * @class Roo.htmleditor.FilterBlock
27007  * removes id / data-block and contenteditable that are associated with blocks
27008  * usage should be done on a cloned copy of the dom
27009  * @constructor
27010 * Run a new Attribute Filter { node : xxxx }}
27011 * @param {Object} config Configuration options
27012  */
27013 Roo.htmleditor.FilterBlock = function(cfg)
27014 {
27015     Roo.apply(this, cfg);
27016     var qa = cfg.node.querySelectorAll;
27017     this.removeAttributes('data-block');
27018     this.removeAttributes('contenteditable');
27019     this.removeAttributes('id');
27020     
27021 }
27022
27023 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27024 {
27025     node: true, // all tags
27026      
27027      
27028     removeAttributes : function(attr)
27029     {
27030         var ar = this.node.querySelectorAll('*[' + attr + ']');
27031         for (var i =0;i<ar.length;i++) {
27032             ar[i].removeAttribute(attr);
27033         }
27034     }
27035         
27036         
27037         
27038     
27039 });
27040 /**
27041  * @class Roo.htmleditor.KeyEnter
27042  * Handle Enter press..
27043  * @cfg {Roo.HtmlEditorCore} core the editor.
27044  * @constructor
27045  * Create a new Filter.
27046  * @param {Object} config Configuration options
27047  */
27048
27049
27050
27051
27052
27053 Roo.htmleditor.KeyEnter = function(cfg) {
27054     Roo.apply(this, cfg);
27055     // this does not actually call walk as it's really just a abstract class
27056  
27057     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27058 }
27059
27060 //Roo.htmleditor.KeyEnter.i = 0;
27061
27062
27063 Roo.htmleditor.KeyEnter.prototype = {
27064     
27065     core : false,
27066     
27067     keypress : function(e)
27068     {
27069         if (e.charCode != 13 && e.charCode != 10) {
27070             Roo.log([e.charCode,e]);
27071             return true;
27072         }
27073         e.preventDefault();
27074         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27075         var doc = this.core.doc;
27076           //add a new line
27077        
27078     
27079         var sel = this.core.getSelection();
27080         var range = sel.getRangeAt(0);
27081         var n = range.commonAncestorContainer;
27082         var pc = range.closest([ 'ol', 'ul']);
27083         var pli = range.closest('li');
27084         if (!pc || e.ctrlKey) {
27085             // on it list, or ctrl pressed.
27086             if (!e.ctrlKey) {
27087                 sel.insertNode('br', 'after'); 
27088             } else {
27089                 // only do this if we have ctrl key..
27090                 var br = doc.createElement('br');
27091                 br.className = 'clear';
27092                 br.setAttribute('style', 'clear: both');
27093                 sel.insertNode(br, 'after'); 
27094             }
27095             
27096          
27097             this.core.undoManager.addEvent();
27098             this.core.fireEditorEvent(e);
27099             return false;
27100         }
27101         
27102         // deal with <li> insetion
27103         if (pli.innerText.trim() == '' &&
27104             pli.previousSibling &&
27105             pli.previousSibling.nodeName == 'LI' &&
27106             pli.previousSibling.innerText.trim() ==  '') {
27107             pli.parentNode.removeChild(pli.previousSibling);
27108             sel.cursorAfter(pc);
27109             this.core.undoManager.addEvent();
27110             this.core.fireEditorEvent(e);
27111             return false;
27112         }
27113     
27114         var li = doc.createElement('LI');
27115         li.innerHTML = '&nbsp;';
27116         if (!pli || !pli.firstSibling) {
27117             pc.appendChild(li);
27118         } else {
27119             pli.parentNode.insertBefore(li, pli.firstSibling);
27120         }
27121         sel.cursorText (li.firstChild);
27122       
27123         this.core.undoManager.addEvent();
27124         this.core.fireEditorEvent(e);
27125
27126         return false;
27127         
27128     
27129         
27130         
27131          
27132     }
27133 };
27134      
27135 /**
27136  * @class Roo.htmleditor.Block
27137  * Base class for html editor blocks - do not use it directly .. extend it..
27138  * @cfg {DomElement} node The node to apply stuff to.
27139  * @cfg {String} friendly_name the name that appears in the context bar about this block
27140  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27141  
27142  * @constructor
27143  * Create a new Filter.
27144  * @param {Object} config Configuration options
27145  */
27146
27147 Roo.htmleditor.Block  = function(cfg)
27148 {
27149     // do nothing .. should not be called really.
27150 }
27151 /**
27152  * factory method to get the block from an element (using cache if necessary)
27153  * @static
27154  * @param {HtmlElement} the dom element
27155  */
27156 Roo.htmleditor.Block.factory = function(node)
27157 {
27158     var cc = Roo.htmleditor.Block.cache;
27159     var id = Roo.get(node).id;
27160     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27161         Roo.htmleditor.Block.cache[id].readElement(node);
27162         return Roo.htmleditor.Block.cache[id];
27163     }
27164     var db  = node.getAttribute('data-block');
27165     if (!db) {
27166         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27167     }
27168     var cls = Roo.htmleditor['Block' + db];
27169     if (typeof(cls) == 'undefined') {
27170         //Roo.log(node.getAttribute('data-block'));
27171         Roo.log("OOps missing block : " + 'Block' + db);
27172         return false;
27173     }
27174     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27175     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27176 };
27177
27178 /**
27179  * initalize all Elements from content that are 'blockable'
27180  * @static
27181  * @param the body element
27182  */
27183 Roo.htmleditor.Block.initAll = function(body, type)
27184 {
27185     if (typeof(type) == 'undefined') {
27186         var ia = Roo.htmleditor.Block.initAll;
27187         ia(body,'table');
27188         ia(body,'td');
27189         ia(body,'figure');
27190         return;
27191     }
27192     Roo.each(Roo.get(body).query(type), function(e) {
27193         Roo.htmleditor.Block.factory(e);    
27194     },this);
27195 };
27196 // question goes here... do we need to clear out this cache sometimes?
27197 // or show we make it relivant to the htmleditor.
27198 Roo.htmleditor.Block.cache = {};
27199
27200 Roo.htmleditor.Block.prototype = {
27201     
27202     node : false,
27203     
27204      // used by context menu
27205     friendly_name : 'Based Block',
27206     
27207     // text for button to delete this element
27208     deleteTitle : false,
27209     
27210     context : false,
27211     /**
27212      * Update a node with values from this object
27213      * @param {DomElement} node
27214      */
27215     updateElement : function(node)
27216     {
27217         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27218     },
27219      /**
27220      * convert to plain HTML for calling insertAtCursor..
27221      */
27222     toHTML : function()
27223     {
27224         return Roo.DomHelper.markup(this.toObject());
27225     },
27226     /**
27227      * used by readEleemnt to extract data from a node
27228      * may need improving as it's pretty basic
27229      
27230      * @param {DomElement} node
27231      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27232      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27233      * @param {String} style the style property - eg. text-align
27234      */
27235     getVal : function(node, tag, attr, style)
27236     {
27237         var n = node;
27238         if (tag !== true && n.tagName != tag.toUpperCase()) {
27239             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27240             // but kiss for now.
27241             n = node.getElementsByTagName(tag).item(0);
27242         }
27243         if (!n) {
27244             return '';
27245         }
27246         if (attr === false) {
27247             return n;
27248         }
27249         if (attr == 'html') {
27250             return n.innerHTML;
27251         }
27252         if (attr == 'style') {
27253             return n.style[style]; 
27254         }
27255         
27256         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27257             
27258     },
27259     /**
27260      * create a DomHelper friendly object - for use with 
27261      * Roo.DomHelper.markup / overwrite / etc..
27262      * (override this)
27263      */
27264     toObject : function()
27265     {
27266         return {};
27267     },
27268       /**
27269      * Read a node that has a 'data-block' property - and extract the values from it.
27270      * @param {DomElement} node - the node
27271      */
27272     readElement : function(node)
27273     {
27274         
27275     } 
27276     
27277     
27278 };
27279
27280  
27281
27282 /**
27283  * @class Roo.htmleditor.BlockFigure
27284  * Block that has an image and a figcaption
27285  * @cfg {String} image_src the url for the image
27286  * @cfg {String} align (left|right) alignment for the block default left
27287  * @cfg {String} caption the text to appear below  (and in the alt tag)
27288  * @cfg {String} caption_display (block|none) display or not the caption
27289  * @cfg {String|number} image_width the width of the image number or %?
27290  * @cfg {String|number} image_height the height of the image number or %?
27291  * 
27292  * @constructor
27293  * Create a new Filter.
27294  * @param {Object} config Configuration options
27295  */
27296
27297 Roo.htmleditor.BlockFigure = function(cfg)
27298 {
27299     if (cfg.node) {
27300         this.readElement(cfg.node);
27301         this.updateElement(cfg.node);
27302     }
27303     Roo.apply(this, cfg);
27304 }
27305 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27306  
27307     
27308     // setable values.
27309     image_src: '',
27310     align: 'center',
27311     caption : '',
27312     caption_display : 'block',
27313     width : '100%',
27314     cls : '',
27315     href: '',
27316     video_url : '',
27317     
27318     // margin: '2%', not used
27319     
27320     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27321
27322     
27323     // used by context menu
27324     friendly_name : 'Image with caption',
27325     deleteTitle : "Delete Image and Caption",
27326     
27327     contextMenu : function(toolbar)
27328     {
27329         
27330         var block = function() {
27331             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27332         };
27333         
27334         
27335         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27336         
27337         var syncValue = toolbar.editorcore.syncValue;
27338         
27339         var fields = {};
27340         
27341         return [
27342              {
27343                 xtype : 'TextItem',
27344                 text : "Source: ",
27345                 xns : rooui.Toolbar  //Boostrap?
27346             },
27347             {
27348                 xtype : 'Button',
27349                 text: 'Change Image URL',
27350                  
27351                 listeners : {
27352                     click: function (btn, state)
27353                     {
27354                         var b = block();
27355                         
27356                         Roo.MessageBox.show({
27357                             title : "Image Source URL",
27358                             msg : "Enter the url for the image",
27359                             buttons: Roo.MessageBox.OKCANCEL,
27360                             fn: function(btn, val){
27361                                 if (btn != 'ok') {
27362                                     return;
27363                                 }
27364                                 b.image_src = val;
27365                                 b.updateElement();
27366                                 syncValue();
27367                                 toolbar.editorcore.onEditorEvent();
27368                             },
27369                             minWidth:250,
27370                             prompt:true,
27371                             //multiline: multiline,
27372                             modal : true,
27373                             value : b.image_src
27374                         });
27375                     }
27376                 },
27377                 xns : rooui.Toolbar
27378             },
27379          
27380             {
27381                 xtype : 'Button',
27382                 text: 'Change Link URL',
27383                  
27384                 listeners : {
27385                     click: function (btn, state)
27386                     {
27387                         var b = block();
27388                         
27389                         Roo.MessageBox.show({
27390                             title : "Link URL",
27391                             msg : "Enter the url for the link - leave blank to have no link",
27392                             buttons: Roo.MessageBox.OKCANCEL,
27393                             fn: function(btn, val){
27394                                 if (btn != 'ok') {
27395                                     return;
27396                                 }
27397                                 b.href = val;
27398                                 b.updateElement();
27399                                 syncValue();
27400                                 toolbar.editorcore.onEditorEvent();
27401                             },
27402                             minWidth:250,
27403                             prompt:true,
27404                             //multiline: multiline,
27405                             modal : true,
27406                             value : b.href
27407                         });
27408                     }
27409                 },
27410                 xns : rooui.Toolbar
27411             },
27412             {
27413                 xtype : 'Button',
27414                 text: 'Show Video URL',
27415                  
27416                 listeners : {
27417                     click: function (btn, state)
27418                     {
27419                         Roo.MessageBox.alert("Video URL",
27420                             block().video_url == '' ? 'This image is not linked ot a video' :
27421                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27422                     }
27423                 },
27424                 xns : rooui.Toolbar
27425             },
27426             
27427             
27428             {
27429                 xtype : 'TextItem',
27430                 text : "Width: ",
27431                 xns : rooui.Toolbar  //Boostrap?
27432             },
27433             {
27434                 xtype : 'ComboBox',
27435                 allowBlank : false,
27436                 displayField : 'val',
27437                 editable : true,
27438                 listWidth : 100,
27439                 triggerAction : 'all',
27440                 typeAhead : true,
27441                 valueField : 'val',
27442                 width : 70,
27443                 name : 'width',
27444                 listeners : {
27445                     select : function (combo, r, index)
27446                     {
27447                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27448                         var b = block();
27449                         b.width = r.get('val');
27450                         b.updateElement();
27451                         syncValue();
27452                         toolbar.editorcore.onEditorEvent();
27453                     }
27454                 },
27455                 xns : rooui.form,
27456                 store : {
27457                     xtype : 'SimpleStore',
27458                     data : [
27459                         ['100%'],
27460                         ['80%'],
27461                         ['50%'],
27462                         ['20%'],
27463                         ['10%']
27464                     ],
27465                     fields : [ 'val'],
27466                     xns : Roo.data
27467                 }
27468             },
27469             {
27470                 xtype : 'TextItem',
27471                 text : "Align: ",
27472                 xns : rooui.Toolbar  //Boostrap?
27473             },
27474             {
27475                 xtype : 'ComboBox',
27476                 allowBlank : false,
27477                 displayField : 'val',
27478                 editable : true,
27479                 listWidth : 100,
27480                 triggerAction : 'all',
27481                 typeAhead : true,
27482                 valueField : 'val',
27483                 width : 70,
27484                 name : 'align',
27485                 listeners : {
27486                     select : function (combo, r, index)
27487                     {
27488                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27489                         var b = block();
27490                         b.align = r.get('val');
27491                         b.updateElement();
27492                         syncValue();
27493                         toolbar.editorcore.onEditorEvent();
27494                     }
27495                 },
27496                 xns : rooui.form,
27497                 store : {
27498                     xtype : 'SimpleStore',
27499                     data : [
27500                         ['left'],
27501                         ['right'],
27502                         ['center']
27503                     ],
27504                     fields : [ 'val'],
27505                     xns : Roo.data
27506                 }
27507             },
27508             
27509             
27510             {
27511                 xtype : 'Button',
27512                 text: 'Hide Caption',
27513                 name : 'caption_display',
27514                 pressed : false,
27515                 enableToggle : true,
27516                 setValue : function(v) {
27517                     // this trigger toggle.
27518                      
27519                     this.setText(v ? "Hide Caption" : "Show Caption");
27520                     this.setPressed(v != 'block');
27521                 },
27522                 listeners : {
27523                     toggle: function (btn, state)
27524                     {
27525                         var b  = block();
27526                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27527                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27528                         b.updateElement();
27529                         syncValue();
27530                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27531                         toolbar.editorcore.onEditorEvent();
27532                     }
27533                 },
27534                 xns : rooui.Toolbar
27535             }
27536         ];
27537         
27538     },
27539     /**
27540      * create a DomHelper friendly object - for use with
27541      * Roo.DomHelper.markup / overwrite / etc..
27542      */
27543     toObject : function()
27544     {
27545         var d = document.createElement('div');
27546         d.innerHTML = this.caption;
27547         
27548         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27549         
27550         var iw = this.align == 'center' ? this.width : '100%';
27551         var img =   {
27552             tag : 'img',
27553             contenteditable : 'false',
27554             src : this.image_src,
27555             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27556             style: {
27557                 width : iw,
27558                 maxWidth : iw + ' !important', // this is not getting rendered?
27559                 margin : m  
27560                 
27561             }
27562         };
27563         /*
27564         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27565                     '<a href="{2}">' + 
27566                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27567                     '</a>' + 
27568                 '</div>',
27569         */
27570                 
27571         if (this.href.length > 0) {
27572             img = {
27573                 tag : 'a',
27574                 href: this.href,
27575                 contenteditable : 'true',
27576                 cn : [
27577                     img
27578                 ]
27579             };
27580         }
27581         
27582         
27583         if (this.video_url.length > 0) {
27584             img = {
27585                 tag : 'div',
27586                 cls : this.cls,
27587                 frameborder : 0,
27588                 allowfullscreen : true,
27589                 width : 420,  // these are for video tricks - that we replace the outer
27590                 height : 315,
27591                 src : this.video_url,
27592                 cn : [
27593                     img
27594                 ]
27595             };
27596         }
27597         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27598         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27599         
27600   
27601         var ret =   {
27602             tag: 'figure',
27603             'data-block' : 'Figure',
27604             'data-width' : this.width, 
27605             contenteditable : 'false',
27606             
27607             style : {
27608                 display: 'block',
27609                 float :  this.align ,
27610                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27611                 width : this.align == 'center' ? '100%' : this.width,
27612                 margin:  '0px',
27613                 padding: this.align == 'center' ? '0' : '0 10px' ,
27614                 textAlign : this.align   // seems to work for email..
27615                 
27616             },
27617            
27618             
27619             align : this.align,
27620             cn : [
27621                 img,
27622               
27623                 {
27624                     tag: 'figcaption',
27625                     'data-display' : this.caption_display,
27626                     style : {
27627                         textAlign : 'left',
27628                         fontSize : '16px',
27629                         lineHeight : '24px',
27630                         display : this.caption_display,
27631                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27632                         margin: m,
27633                         width: this.align == 'center' ?  this.width : '100%' 
27634                     
27635                          
27636                     },
27637                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27638                     cn : [
27639                         {
27640                             tag: 'div',
27641                             style  : {
27642                                 marginTop : '16px',
27643                                 textAlign : 'left'
27644                             },
27645                             align: 'left',
27646                             cn : [
27647                                 {
27648                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27649                                     tag : 'i',
27650                                     contenteditable : true,
27651                                     html : captionhtml
27652                                 }
27653                                 
27654                             ]
27655                         }
27656                         
27657                     ]
27658                     
27659                 }
27660             ]
27661         };
27662         return ret;
27663          
27664     },
27665     
27666     readElement : function(node)
27667     {
27668         // this should not really come from the link...
27669         this.video_url = this.getVal(node, 'div', 'src');
27670         this.cls = this.getVal(node, 'div', 'class');
27671         this.href = this.getVal(node, 'a', 'href');
27672         
27673         
27674         this.image_src = this.getVal(node, 'img', 'src');
27675          
27676         this.align = this.getVal(node, 'figure', 'align');
27677         var figcaption = this.getVal(node, 'figcaption', false);
27678         if (figcaption !== '') {
27679             this.caption = this.getVal(figcaption, 'i', 'html');
27680         }
27681         
27682
27683         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27684         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27685         this.width = this.getVal(node, true, 'data-width');
27686         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27687         
27688     },
27689     removeNode : function()
27690     {
27691         return this.node;
27692     }
27693     
27694   
27695    
27696      
27697     
27698     
27699     
27700     
27701 })
27702
27703  
27704
27705 /**
27706  * @class Roo.htmleditor.BlockTable
27707  * Block that manages a table
27708  * 
27709  * @constructor
27710  * Create a new Filter.
27711  * @param {Object} config Configuration options
27712  */
27713
27714 Roo.htmleditor.BlockTable = function(cfg)
27715 {
27716     if (cfg.node) {
27717         this.readElement(cfg.node);
27718         this.updateElement(cfg.node);
27719     }
27720     Roo.apply(this, cfg);
27721     if (!cfg.node) {
27722         this.rows = [];
27723         for(var r = 0; r < this.no_row; r++) {
27724             this.rows[r] = [];
27725             for(var c = 0; c < this.no_col; c++) {
27726                 this.rows[r][c] = this.emptyCell();
27727             }
27728         }
27729     }
27730     
27731     
27732 }
27733 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27734  
27735     rows : false,
27736     no_col : 1,
27737     no_row : 1,
27738     
27739     
27740     width: '100%',
27741     
27742     // used by context menu
27743     friendly_name : 'Table',
27744     deleteTitle : 'Delete Table',
27745     // context menu is drawn once..
27746     
27747     contextMenu : function(toolbar)
27748     {
27749         
27750         var block = function() {
27751             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27752         };
27753         
27754         
27755         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27756         
27757         var syncValue = toolbar.editorcore.syncValue;
27758         
27759         var fields = {};
27760         
27761         return [
27762             {
27763                 xtype : 'TextItem',
27764                 text : "Width: ",
27765                 xns : rooui.Toolbar  //Boostrap?
27766             },
27767             {
27768                 xtype : 'ComboBox',
27769                 allowBlank : false,
27770                 displayField : 'val',
27771                 editable : true,
27772                 listWidth : 100,
27773                 triggerAction : 'all',
27774                 typeAhead : true,
27775                 valueField : 'val',
27776                 width : 100,
27777                 name : 'width',
27778                 listeners : {
27779                     select : function (combo, r, index)
27780                     {
27781                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27782                         var b = block();
27783                         b.width = r.get('val');
27784                         b.updateElement();
27785                         syncValue();
27786                         toolbar.editorcore.onEditorEvent();
27787                     }
27788                 },
27789                 xns : rooui.form,
27790                 store : {
27791                     xtype : 'SimpleStore',
27792                     data : [
27793                         ['100%'],
27794                         ['auto']
27795                     ],
27796                     fields : [ 'val'],
27797                     xns : Roo.data
27798                 }
27799             },
27800             // -------- Cols
27801             
27802             {
27803                 xtype : 'TextItem',
27804                 text : "Columns: ",
27805                 xns : rooui.Toolbar  //Boostrap?
27806             },
27807          
27808             {
27809                 xtype : 'Button',
27810                 text: '-',
27811                 listeners : {
27812                     click : function (_self, e)
27813                     {
27814                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27815                         block().removeColumn();
27816                         syncValue();
27817                         toolbar.editorcore.onEditorEvent();
27818                     }
27819                 },
27820                 xns : rooui.Toolbar
27821             },
27822             {
27823                 xtype : 'Button',
27824                 text: '+',
27825                 listeners : {
27826                     click : function (_self, e)
27827                     {
27828                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27829                         block().addColumn();
27830                         syncValue();
27831                         toolbar.editorcore.onEditorEvent();
27832                     }
27833                 },
27834                 xns : rooui.Toolbar
27835             },
27836             // -------- ROWS
27837             {
27838                 xtype : 'TextItem',
27839                 text : "Rows: ",
27840                 xns : rooui.Toolbar  //Boostrap?
27841             },
27842          
27843             {
27844                 xtype : 'Button',
27845                 text: '-',
27846                 listeners : {
27847                     click : function (_self, e)
27848                     {
27849                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27850                         block().removeRow();
27851                         syncValue();
27852                         toolbar.editorcore.onEditorEvent();
27853                     }
27854                 },
27855                 xns : rooui.Toolbar
27856             },
27857             {
27858                 xtype : 'Button',
27859                 text: '+',
27860                 listeners : {
27861                     click : function (_self, e)
27862                     {
27863                         block().addRow();
27864                         syncValue();
27865                         toolbar.editorcore.onEditorEvent();
27866                     }
27867                 },
27868                 xns : rooui.Toolbar
27869             },
27870             // -------- ROWS
27871             {
27872                 xtype : 'Button',
27873                 text: 'Reset Column Widths',
27874                 listeners : {
27875                     
27876                     click : function (_self, e)
27877                     {
27878                         block().resetWidths();
27879                         syncValue();
27880                         toolbar.editorcore.onEditorEvent();
27881                     }
27882                 },
27883                 xns : rooui.Toolbar
27884             } 
27885             
27886             
27887             
27888         ];
27889         
27890     },
27891     
27892     
27893   /**
27894      * create a DomHelper friendly object - for use with
27895      * Roo.DomHelper.markup / overwrite / etc..
27896      * ?? should it be called with option to hide all editing features?
27897      */
27898     toObject : function()
27899     {
27900         
27901         var ret = {
27902             tag : 'table',
27903             contenteditable : 'false', // this stops cell selection from picking the table.
27904             'data-block' : 'Table',
27905             style : {
27906                 width:  this.width,
27907                 border : 'solid 1px #000', // ??? hard coded?
27908                 'border-collapse' : 'collapse' 
27909             },
27910             cn : [
27911                 { tag : 'tbody' , cn : [] }
27912             ]
27913         };
27914         
27915         // do we have a head = not really 
27916         var ncols = 0;
27917         Roo.each(this.rows, function( row ) {
27918             var tr = {
27919                 tag: 'tr',
27920                 style : {
27921                     margin: '6px',
27922                     border : 'solid 1px #000',
27923                     textAlign : 'left' 
27924                 },
27925                 cn : [ ]
27926             };
27927             
27928             ret.cn[0].cn.push(tr);
27929             // does the row have any properties? ?? height?
27930             var nc = 0;
27931             Roo.each(row, function( cell ) {
27932                 
27933                 var td = {
27934                     tag : 'td',
27935                     contenteditable :  'true',
27936                     'data-block' : 'Td',
27937                     html : cell.html,
27938                     style : cell.style
27939                 };
27940                 if (cell.colspan > 1) {
27941                     td.colspan = cell.colspan ;
27942                     nc += cell.colspan;
27943                 } else {
27944                     nc++;
27945                 }
27946                 if (cell.rowspan > 1) {
27947                     td.rowspan = cell.rowspan ;
27948                 }
27949                 
27950                 
27951                 // widths ?
27952                 tr.cn.push(td);
27953                     
27954                 
27955             }, this);
27956             ncols = Math.max(nc, ncols);
27957             
27958             
27959         }, this);
27960         // add the header row..
27961         
27962         ncols++;
27963          
27964         
27965         return ret;
27966          
27967     },
27968     
27969     readElement : function(node)
27970     {
27971         node  = node ? node : this.node ;
27972         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27973         
27974         this.rows = [];
27975         this.no_row = 0;
27976         var trs = Array.from(node.rows);
27977         trs.forEach(function(tr) {
27978             var row =  [];
27979             this.rows.push(row);
27980             
27981             this.no_row++;
27982             var no_column = 0;
27983             Array.from(tr.cells).forEach(function(td) {
27984                 
27985                 var add = {
27986                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27987                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27988                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27989                     html : td.innerHTML
27990                 };
27991                 no_column += add.colspan;
27992                      
27993                 
27994                 row.push(add);
27995                 
27996                 
27997             },this);
27998             this.no_col = Math.max(this.no_col, no_column);
27999             
28000             
28001         },this);
28002         
28003         
28004     },
28005     normalizeRows: function()
28006     {
28007         var ret= [];
28008         var rid = -1;
28009         this.rows.forEach(function(row) {
28010             rid++;
28011             ret[rid] = [];
28012             row = this.normalizeRow(row);
28013             var cid = 0;
28014             row.forEach(function(c) {
28015                 while (typeof(ret[rid][cid]) != 'undefined') {
28016                     cid++;
28017                 }
28018                 if (typeof(ret[rid]) == 'undefined') {
28019                     ret[rid] = [];
28020                 }
28021                 ret[rid][cid] = c;
28022                 c.row = rid;
28023                 c.col = cid;
28024                 if (c.rowspan < 2) {
28025                     return;
28026                 }
28027                 
28028                 for(var i = 1 ;i < c.rowspan; i++) {
28029                     if (typeof(ret[rid+i]) == 'undefined') {
28030                         ret[rid+i] = [];
28031                     }
28032                     ret[rid+i][cid] = c;
28033                 }
28034             });
28035         }, this);
28036         return ret;
28037     
28038     },
28039     
28040     normalizeRow: function(row)
28041     {
28042         var ret= [];
28043         row.forEach(function(c) {
28044             if (c.colspan < 2) {
28045                 ret.push(c);
28046                 return;
28047             }
28048             for(var i =0 ;i < c.colspan; i++) {
28049                 ret.push(c);
28050             }
28051         });
28052         return ret;
28053     
28054     },
28055     
28056     deleteColumn : function(sel)
28057     {
28058         if (!sel || sel.type != 'col') {
28059             return;
28060         }
28061         if (this.no_col < 2) {
28062             return;
28063         }
28064         
28065         this.rows.forEach(function(row) {
28066             var cols = this.normalizeRow(row);
28067             var col = cols[sel.col];
28068             if (col.colspan > 1) {
28069                 col.colspan --;
28070             } else {
28071                 row.remove(col);
28072             }
28073             
28074         }, this);
28075         this.no_col--;
28076         
28077     },
28078     removeColumn : function()
28079     {
28080         this.deleteColumn({
28081             type: 'col',
28082             col : this.no_col-1
28083         });
28084         this.updateElement();
28085     },
28086     
28087      
28088     addColumn : function()
28089     {
28090         
28091         this.rows.forEach(function(row) {
28092             row.push(this.emptyCell());
28093            
28094         }, this);
28095         this.updateElement();
28096     },
28097     
28098     deleteRow : function(sel)
28099     {
28100         if (!sel || sel.type != 'row') {
28101             return;
28102         }
28103         
28104         if (this.no_row < 2) {
28105             return;
28106         }
28107         
28108         var rows = this.normalizeRows();
28109         
28110         
28111         rows[sel.row].forEach(function(col) {
28112             if (col.rowspan > 1) {
28113                 col.rowspan--;
28114             } else {
28115                 col.remove = 1; // flage it as removed.
28116             }
28117             
28118         }, this);
28119         var newrows = [];
28120         this.rows.forEach(function(row) {
28121             newrow = [];
28122             row.forEach(function(c) {
28123                 if (typeof(c.remove) == 'undefined') {
28124                     newrow.push(c);
28125                 }
28126                 
28127             });
28128             if (newrow.length > 0) {
28129                 newrows.push(row);
28130             }
28131         });
28132         this.rows =  newrows;
28133         
28134         
28135         
28136         this.no_row--;
28137         this.updateElement();
28138         
28139     },
28140     removeRow : function()
28141     {
28142         this.deleteRow({
28143             type: 'row',
28144             row : this.no_row-1
28145         });
28146         
28147     },
28148     
28149      
28150     addRow : function()
28151     {
28152         
28153         var row = [];
28154         for (var i = 0; i < this.no_col; i++ ) {
28155             
28156             row.push(this.emptyCell());
28157            
28158         }
28159         this.rows.push(row);
28160         this.updateElement();
28161         
28162     },
28163      
28164     // the default cell object... at present...
28165     emptyCell : function() {
28166         return (new Roo.htmleditor.BlockTd({})).toObject();
28167         
28168      
28169     },
28170     
28171     removeNode : function()
28172     {
28173         return this.node;
28174     },
28175     
28176     
28177     
28178     resetWidths : function()
28179     {
28180         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28181             var nn = Roo.htmleditor.Block.factory(n);
28182             nn.width = '';
28183             nn.updateElement(n);
28184         });
28185     }
28186     
28187     
28188     
28189     
28190 })
28191
28192 /**
28193  *
28194  * editing a TD?
28195  *
28196  * since selections really work on the table cell, then editing really should work from there
28197  *
28198  * The original plan was to support merging etc... - but that may not be needed yet..
28199  *
28200  * So this simple version will support:
28201  *   add/remove cols
28202  *   adjust the width +/-
28203  *   reset the width...
28204  *   
28205  *
28206  */
28207
28208
28209  
28210
28211 /**
28212  * @class Roo.htmleditor.BlockTable
28213  * Block that manages a table
28214  * 
28215  * @constructor
28216  * Create a new Filter.
28217  * @param {Object} config Configuration options
28218  */
28219
28220 Roo.htmleditor.BlockTd = function(cfg)
28221 {
28222     if (cfg.node) {
28223         this.readElement(cfg.node);
28224         this.updateElement(cfg.node);
28225     }
28226     Roo.apply(this, cfg);
28227      
28228     
28229     
28230 }
28231 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28232  
28233     node : false,
28234     
28235     width: '',
28236     textAlign : 'left',
28237     valign : 'top',
28238     
28239     colspan : 1,
28240     rowspan : 1,
28241     
28242     
28243     // used by context menu
28244     friendly_name : 'Table Cell',
28245     deleteTitle : false, // use our customer delete
28246     
28247     // context menu is drawn once..
28248     
28249     contextMenu : function(toolbar)
28250     {
28251         
28252         var cell = function() {
28253             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28254         };
28255         
28256         var table = function() {
28257             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28258         };
28259         
28260         var lr = false;
28261         var saveSel = function()
28262         {
28263             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28264         }
28265         var restoreSel = function()
28266         {
28267             if (lr) {
28268                 (function() {
28269                     toolbar.editorcore.focus();
28270                     var cr = toolbar.editorcore.getSelection();
28271                     cr.removeAllRanges();
28272                     cr.addRange(lr);
28273                     toolbar.editorcore.onEditorEvent();
28274                 }).defer(10, this);
28275                 
28276                 
28277             }
28278         }
28279         
28280         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28281         
28282         var syncValue = toolbar.editorcore.syncValue;
28283         
28284         var fields = {};
28285         
28286         return [
28287             {
28288                 xtype : 'Button',
28289                 text : 'Edit Table',
28290                 listeners : {
28291                     click : function() {
28292                         var t = toolbar.tb.selectedNode.closest('table');
28293                         toolbar.editorcore.selectNode(t);
28294                         toolbar.editorcore.onEditorEvent();                        
28295                     }
28296                 }
28297                 
28298             },
28299               
28300            
28301              
28302             {
28303                 xtype : 'TextItem',
28304                 text : "Column Width: ",
28305                  xns : rooui.Toolbar 
28306                
28307             },
28308             {
28309                 xtype : 'Button',
28310                 text: '-',
28311                 listeners : {
28312                     click : function (_self, e)
28313                     {
28314                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28315                         cell().shrinkColumn();
28316                         syncValue();
28317                          toolbar.editorcore.onEditorEvent();
28318                     }
28319                 },
28320                 xns : rooui.Toolbar
28321             },
28322             {
28323                 xtype : 'Button',
28324                 text: '+',
28325                 listeners : {
28326                     click : function (_self, e)
28327                     {
28328                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28329                         cell().growColumn();
28330                         syncValue();
28331                         toolbar.editorcore.onEditorEvent();
28332                     }
28333                 },
28334                 xns : rooui.Toolbar
28335             },
28336             
28337             {
28338                 xtype : 'TextItem',
28339                 text : "Vertical Align: ",
28340                 xns : rooui.Toolbar  //Boostrap?
28341             },
28342             {
28343                 xtype : 'ComboBox',
28344                 allowBlank : false,
28345                 displayField : 'val',
28346                 editable : true,
28347                 listWidth : 100,
28348                 triggerAction : 'all',
28349                 typeAhead : true,
28350                 valueField : 'val',
28351                 width : 100,
28352                 name : 'valign',
28353                 listeners : {
28354                     select : function (combo, r, index)
28355                     {
28356                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28357                         var b = cell();
28358                         b.valign = r.get('val');
28359                         b.updateElement();
28360                         syncValue();
28361                         toolbar.editorcore.onEditorEvent();
28362                     }
28363                 },
28364                 xns : rooui.form,
28365                 store : {
28366                     xtype : 'SimpleStore',
28367                     data : [
28368                         ['top'],
28369                         ['middle'],
28370                         ['bottom'] // there are afew more... 
28371                     ],
28372                     fields : [ 'val'],
28373                     xns : Roo.data
28374                 }
28375             },
28376             
28377             {
28378                 xtype : 'TextItem',
28379                 text : "Merge Cells: ",
28380                  xns : rooui.Toolbar 
28381                
28382             },
28383             
28384             
28385             {
28386                 xtype : 'Button',
28387                 text: 'Right',
28388                 listeners : {
28389                     click : function (_self, e)
28390                     {
28391                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28392                         cell().mergeRight();
28393                         //block().growColumn();
28394                         syncValue();
28395                         toolbar.editorcore.onEditorEvent();
28396                     }
28397                 },
28398                 xns : rooui.Toolbar
28399             },
28400              
28401             {
28402                 xtype : 'Button',
28403                 text: 'Below',
28404                 listeners : {
28405                     click : function (_self, e)
28406                     {
28407                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28408                         cell().mergeBelow();
28409                         //block().growColumn();
28410                         syncValue();
28411                         toolbar.editorcore.onEditorEvent();
28412                     }
28413                 },
28414                 xns : rooui.Toolbar
28415             },
28416             {
28417                 xtype : 'TextItem',
28418                 text : "| ",
28419                  xns : rooui.Toolbar 
28420                
28421             },
28422             
28423             {
28424                 xtype : 'Button',
28425                 text: 'Split',
28426                 listeners : {
28427                     click : function (_self, e)
28428                     {
28429                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28430                         cell().split();
28431                         syncValue();
28432                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28433                         toolbar.editorcore.onEditorEvent();
28434                                              
28435                     }
28436                 },
28437                 xns : rooui.Toolbar
28438             },
28439             {
28440                 xtype : 'Fill',
28441                 xns : rooui.Toolbar 
28442                
28443             },
28444         
28445           
28446             {
28447                 xtype : 'Button',
28448                 text: 'Delete',
28449                  
28450                 xns : rooui.Toolbar,
28451                 menu : {
28452                     xtype : 'Menu',
28453                     xns : rooui.menu,
28454                     items : [
28455                         {
28456                             xtype : 'Item',
28457                             html: 'Column',
28458                             listeners : {
28459                                 click : function (_self, e)
28460                                 {
28461                                     var t = table();
28462                                     
28463                                     cell().deleteColumn();
28464                                     syncValue();
28465                                     toolbar.editorcore.selectNode(t.node);
28466                                     toolbar.editorcore.onEditorEvent();   
28467                                 }
28468                             },
28469                             xns : rooui.menu
28470                         },
28471                         {
28472                             xtype : 'Item',
28473                             html: 'Row',
28474                             listeners : {
28475                                 click : function (_self, e)
28476                                 {
28477                                     var t = table();
28478                                     cell().deleteRow();
28479                                     syncValue();
28480                                     
28481                                     toolbar.editorcore.selectNode(t.node);
28482                                     toolbar.editorcore.onEditorEvent();   
28483                                                          
28484                                 }
28485                             },
28486                             xns : rooui.menu
28487                         },
28488                        {
28489                             xtype : 'Separator',
28490                             xns : rooui.menu
28491                         },
28492                         {
28493                             xtype : 'Item',
28494                             html: 'Table',
28495                             listeners : {
28496                                 click : function (_self, e)
28497                                 {
28498                                     var t = table();
28499                                     var nn = t.node.nextSibling || t.node.previousSibling;
28500                                     t.node.parentNode.removeChild(t.node);
28501                                     if (nn) { 
28502                                         toolbar.editorcore.selectNode(nn, true);
28503                                     }
28504                                     toolbar.editorcore.onEditorEvent();   
28505                                                          
28506                                 }
28507                             },
28508                             xns : rooui.menu
28509                         }
28510                     ]
28511                 }
28512             }
28513             
28514             // align... << fixme
28515             
28516         ];
28517         
28518     },
28519     
28520     
28521   /**
28522      * create a DomHelper friendly object - for use with
28523      * Roo.DomHelper.markup / overwrite / etc..
28524      * ?? should it be called with option to hide all editing features?
28525      */
28526  /**
28527      * create a DomHelper friendly object - for use with
28528      * Roo.DomHelper.markup / overwrite / etc..
28529      * ?? should it be called with option to hide all editing features?
28530      */
28531     toObject : function()
28532     {
28533         var ret = {
28534             tag : 'td',
28535             contenteditable : 'true', // this stops cell selection from picking the table.
28536             'data-block' : 'Td',
28537             valign : this.valign,
28538             style : {  
28539                 'text-align' :  this.textAlign,
28540                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28541                 'border-collapse' : 'collapse',
28542                 padding : '6px', // 8 for desktop / 4 for mobile
28543                 'vertical-align': this.valign
28544             },
28545             html : this.html
28546         };
28547         if (this.width != '') {
28548             ret.width = this.width;
28549             ret.style.width = this.width;
28550         }
28551         
28552         
28553         if (this.colspan > 1) {
28554             ret.colspan = this.colspan ;
28555         } 
28556         if (this.rowspan > 1) {
28557             ret.rowspan = this.rowspan ;
28558         }
28559         
28560            
28561         
28562         return ret;
28563          
28564     },
28565     
28566     readElement : function(node)
28567     {
28568         node  = node ? node : this.node ;
28569         this.width = node.style.width;
28570         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28571         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28572         this.html = node.innerHTML;
28573         if (node.style.textAlign != '') {
28574             this.textAlign = node.style.textAlign;
28575         }
28576         
28577         
28578     },
28579      
28580     // the default cell object... at present...
28581     emptyCell : function() {
28582         return {
28583             colspan :  1,
28584             rowspan :  1,
28585             textAlign : 'left',
28586             html : "&nbsp;" // is this going to be editable now?
28587         };
28588      
28589     },
28590     
28591     removeNode : function()
28592     {
28593         return this.node.closest('table');
28594          
28595     },
28596     
28597     cellData : false,
28598     
28599     colWidths : false,
28600     
28601     toTableArray  : function()
28602     {
28603         var ret = [];
28604         var tab = this.node.closest('tr').closest('table');
28605         Array.from(tab.rows).forEach(function(r, ri){
28606             ret[ri] = [];
28607         });
28608         var rn = 0;
28609         this.colWidths = [];
28610         var all_auto = true;
28611         Array.from(tab.rows).forEach(function(r, ri){
28612             
28613             var cn = 0;
28614             Array.from(r.cells).forEach(function(ce, ci){
28615                 var c =  {
28616                     cell : ce,
28617                     row : rn,
28618                     col: cn,
28619                     colspan : ce.colSpan,
28620                     rowspan : ce.rowSpan
28621                 };
28622                 if (ce.isEqualNode(this.node)) {
28623                     this.cellData = c;
28624                 }
28625                 // if we have been filled up by a row?
28626                 if (typeof(ret[rn][cn]) != 'undefined') {
28627                     while(typeof(ret[rn][cn]) != 'undefined') {
28628                         cn++;
28629                     }
28630                     c.col = cn;
28631                 }
28632                 
28633                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
28634                     this.colWidths[cn] =   ce.style.width;
28635                     if (this.colWidths[cn] != '') {
28636                         all_auto = false;
28637                     }
28638                 }
28639                 
28640                 
28641                 if (c.colspan < 2 && c.rowspan < 2 ) {
28642                     ret[rn][cn] = c;
28643                     cn++;
28644                     return;
28645                 }
28646                 for(var j = 0; j < c.rowspan; j++) {
28647                     if (typeof(ret[rn+j]) == 'undefined') {
28648                         continue; // we have a problem..
28649                     }
28650                     ret[rn+j][cn] = c;
28651                     for(var i = 0; i < c.colspan; i++) {
28652                         ret[rn+j][cn+i] = c;
28653                     }
28654                 }
28655                 
28656                 cn += c.colspan;
28657             }, this);
28658             rn++;
28659         }, this);
28660         
28661         // initalize widths.?
28662         // either all widths or no widths..
28663         if (all_auto) {
28664             this.colWidths[0] = false; // no widths flag.
28665         }
28666         
28667         
28668         return ret;
28669         
28670     },
28671     
28672     
28673     
28674     
28675     mergeRight: function()
28676     {
28677          
28678         // get the contents of the next cell along..
28679         var tr = this.node.closest('tr');
28680         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28681         if (i >= tr.childNodes.length - 1) {
28682             return; // no cells on right to merge with.
28683         }
28684         var table = this.toTableArray();
28685         
28686         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28687             return; // nothing right?
28688         }
28689         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28690         // right cell - must be same rowspan and on the same row.
28691         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28692             return; // right hand side is not same rowspan.
28693         }
28694         
28695         
28696         
28697         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28698         tr.removeChild(rc.cell);
28699         this.colspan += rc.colspan;
28700         this.node.setAttribute('colspan', this.colspan);
28701
28702         var table = this.toTableArray();
28703         this.normalizeWidths(table);
28704         this.updateWidths(table);
28705     },
28706     
28707     
28708     mergeBelow : function()
28709     {
28710         var table = this.toTableArray();
28711         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28712             return; // no row below
28713         }
28714         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28715             return; // nothing right?
28716         }
28717         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28718         
28719         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28720             return; // right hand side is not same rowspan.
28721         }
28722         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28723         rc.cell.parentNode.removeChild(rc.cell);
28724         this.rowspan += rc.rowspan;
28725         this.node.setAttribute('rowspan', this.rowspan);
28726     },
28727     
28728     split: function()
28729     {
28730         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28731             return;
28732         }
28733         var table = this.toTableArray();
28734         var cd = this.cellData;
28735         this.rowspan = 1;
28736         this.colspan = 1;
28737         
28738         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28739              
28740             
28741             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28742                 if (r == cd.row && c == cd.col) {
28743                     this.node.removeAttribute('rowspan');
28744                     this.node.removeAttribute('colspan');
28745                 }
28746                  
28747                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28748                 ntd.removeAttribute('id'); 
28749                 ntd.style.width  = this.colWidths[c];
28750                 ntd.innerHTML = '';
28751                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28752             }
28753             
28754         }
28755         this.redrawAllCells(table);
28756         
28757     },
28758     
28759     
28760     
28761     redrawAllCells: function(table)
28762     {
28763         
28764          
28765         var tab = this.node.closest('tr').closest('table');
28766         var ctr = tab.rows[0].parentNode;
28767         Array.from(tab.rows).forEach(function(r, ri){
28768             
28769             Array.from(r.cells).forEach(function(ce, ci){
28770                 ce.parentNode.removeChild(ce);
28771             });
28772             r.parentNode.removeChild(r);
28773         });
28774         for(var r = 0 ; r < table.length; r++) {
28775             var re = tab.rows[r];
28776             
28777             var re = tab.ownerDocument.createElement('tr');
28778             ctr.appendChild(re);
28779             for(var c = 0 ; c < table[r].length; c++) {
28780                 if (table[r][c].cell === false) {
28781                     continue;
28782                 }
28783                 
28784                 re.appendChild(table[r][c].cell);
28785                  
28786                 table[r][c].cell = false;
28787             }
28788         }
28789         
28790     },
28791     updateWidths : function(table)
28792     {
28793         for(var r = 0 ; r < table.length; r++) {
28794            
28795             for(var c = 0 ; c < table[r].length; c++) {
28796                 if (table[r][c].cell === false) {
28797                     continue;
28798                 }
28799                 
28800                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28801                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28802                     el.width = Math.floor(this.colWidths[c])  +'%';
28803                     el.updateElement(el.node);
28804                 }
28805                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
28806                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28807                     var width = 0;
28808                     for(var i = 0; i < table[r][c].colspan; i ++) {
28809                         width += Math.floor(this.colWidths[c + i]);
28810                     }
28811                     el.width = width  +'%';
28812                     el.updateElement(el.node);
28813                 }
28814                 table[r][c].cell = false; // done
28815             }
28816         }
28817     },
28818     normalizeWidths : function(table)
28819     {
28820         if (this.colWidths[0] === false) {
28821             var nw = 100.0 / this.colWidths.length;
28822             this.colWidths.forEach(function(w,i) {
28823                 this.colWidths[i] = nw;
28824             },this);
28825             return;
28826         }
28827     
28828         var t = 0, missing = [];
28829         
28830         this.colWidths.forEach(function(w,i) {
28831             //if you mix % and
28832             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28833             var add =  this.colWidths[i];
28834             if (add > 0) {
28835                 t+=add;
28836                 return;
28837             }
28838             missing.push(i);
28839             
28840             
28841         },this);
28842         var nc = this.colWidths.length;
28843         if (missing.length) {
28844             var mult = (nc - missing.length) / (1.0 * nc);
28845             var t = mult * t;
28846             var ew = (100 -t) / (1.0 * missing.length);
28847             this.colWidths.forEach(function(w,i) {
28848                 if (w > 0) {
28849                     this.colWidths[i] = w * mult;
28850                     return;
28851                 }
28852                 
28853                 this.colWidths[i] = ew;
28854             }, this);
28855             // have to make up numbers..
28856              
28857         }
28858         // now we should have all the widths..
28859         
28860     
28861     },
28862     
28863     shrinkColumn : function()
28864     {
28865         var table = this.toTableArray();
28866         this.normalizeWidths(table);
28867         var col = this.cellData.col;
28868         var nw = this.colWidths[col] * 0.8;
28869         if (nw < 5) {
28870             return;
28871         }
28872         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28873         this.colWidths.forEach(function(w,i) {
28874             if (i == col) {
28875                  this.colWidths[i] = nw;
28876                 return;
28877             }
28878             this.colWidths[i] += otherAdd
28879         }, this);
28880         this.updateWidths(table);
28881          
28882     },
28883     growColumn : function()
28884     {
28885         var table = this.toTableArray();
28886         this.normalizeWidths(table);
28887         var col = this.cellData.col;
28888         var nw = this.colWidths[col] * 1.2;
28889         if (nw > 90) {
28890             return;
28891         }
28892         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28893         this.colWidths.forEach(function(w,i) {
28894             if (i == col) {
28895                 this.colWidths[i] = nw;
28896                 return;
28897             }
28898             this.colWidths[i] -= otherSub
28899         }, this);
28900         this.updateWidths(table);
28901          
28902     },
28903     deleteRow : function()
28904     {
28905         // delete this rows 'tr'
28906         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28907         // then reduce the rowspan.
28908         var table = this.toTableArray();
28909         // this.cellData.row;
28910         for (var i =0;i< table[this.cellData.row].length ; i++) {
28911             var c = table[this.cellData.row][i];
28912             if (c.row != this.cellData.row) {
28913                 
28914                 c.rowspan--;
28915                 c.cell.setAttribute('rowspan', c.rowspan);
28916                 continue;
28917             }
28918             if (c.rowspan > 1) {
28919                 c.rowspan--;
28920                 c.cell.setAttribute('rowspan', c.rowspan);
28921             }
28922         }
28923         table.splice(this.cellData.row,1);
28924         this.redrawAllCells(table);
28925         
28926     },
28927     deleteColumn : function()
28928     {
28929         var table = this.toTableArray();
28930         
28931         for (var i =0;i< table.length ; i++) {
28932             var c = table[i][this.cellData.col];
28933             if (c.col != this.cellData.col) {
28934                 table[i][this.cellData.col].colspan--;
28935             } else if (c.colspan > 1) {
28936                 c.colspan--;
28937                 c.cell.setAttribute('colspan', c.colspan);
28938             }
28939             table[i].splice(this.cellData.col,1);
28940         }
28941         
28942         this.redrawAllCells(table);
28943     }
28944     
28945     
28946     
28947     
28948 })
28949
28950 //<script type="text/javascript">
28951
28952 /*
28953  * Based  Ext JS Library 1.1.1
28954  * Copyright(c) 2006-2007, Ext JS, LLC.
28955  * LGPL
28956  *
28957  */
28958  
28959 /**
28960  * @class Roo.HtmlEditorCore
28961  * @extends Roo.Component
28962  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28963  *
28964  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28965  */
28966
28967 Roo.HtmlEditorCore = function(config){
28968     
28969     
28970     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28971     
28972     
28973     this.addEvents({
28974         /**
28975          * @event initialize
28976          * Fires when the editor is fully initialized (including the iframe)
28977          * @param {Roo.HtmlEditorCore} this
28978          */
28979         initialize: true,
28980         /**
28981          * @event activate
28982          * Fires when the editor is first receives the focus. Any insertion must wait
28983          * until after this event.
28984          * @param {Roo.HtmlEditorCore} this
28985          */
28986         activate: true,
28987          /**
28988          * @event beforesync
28989          * Fires before the textarea is updated with content from the editor iframe. Return false
28990          * to cancel the sync.
28991          * @param {Roo.HtmlEditorCore} this
28992          * @param {String} html
28993          */
28994         beforesync: true,
28995          /**
28996          * @event beforepush
28997          * Fires before the iframe editor is updated with content from the textarea. Return false
28998          * to cancel the push.
28999          * @param {Roo.HtmlEditorCore} this
29000          * @param {String} html
29001          */
29002         beforepush: true,
29003          /**
29004          * @event sync
29005          * Fires when the textarea is updated with content from the editor iframe.
29006          * @param {Roo.HtmlEditorCore} this
29007          * @param {String} html
29008          */
29009         sync: true,
29010          /**
29011          * @event push
29012          * Fires when the iframe editor is updated with content from the textarea.
29013          * @param {Roo.HtmlEditorCore} this
29014          * @param {String} html
29015          */
29016         push: true,
29017         
29018         /**
29019          * @event editorevent
29020          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29021          * @param {Roo.HtmlEditorCore} this
29022          */
29023         editorevent: true 
29024          
29025         
29026     });
29027     
29028     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29029     
29030     // defaults : white / black...
29031     this.applyBlacklists();
29032     
29033     
29034     
29035 };
29036
29037
29038 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
29039
29040
29041      /**
29042      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
29043      */
29044     
29045     owner : false,
29046     
29047      /**
29048      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
29049      *                        Roo.resizable.
29050      */
29051     resizable : false,
29052      /**
29053      * @cfg {Number} height (in pixels)
29054      */   
29055     height: 300,
29056    /**
29057      * @cfg {Number} width (in pixels)
29058      */   
29059     width: 500,
29060      /**
29061      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29062      *         if you are doing an email editor, this probably needs disabling, it's designed
29063      */
29064     autoClean: true,
29065     
29066     /**
29067      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29068      */
29069     enableBlocks : true,
29070     /**
29071      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29072      * 
29073      */
29074     stylesheets: false,
29075      /**
29076      * @cfg {String} language default en - language of text (usefull for rtl languages)
29077      * 
29078      */
29079     language: 'en',
29080     
29081     /**
29082      * @cfg {boolean} allowComments - default false - allow comments in HTML source
29083      *          - by default they are stripped - if you are editing email you may need this.
29084      */
29085     allowComments: false,
29086     // id of frame..
29087     frameId: false,
29088     
29089     // private properties
29090     validationEvent : false,
29091     deferHeight: true,
29092     initialized : false,
29093     activated : false,
29094     sourceEditMode : false,
29095     onFocus : Roo.emptyFn,
29096     iframePad:3,
29097     hideMode:'offsets',
29098     
29099     clearUp: true,
29100     
29101     // blacklist + whitelisted elements..
29102     black: false,
29103     white: false,
29104      
29105     bodyCls : '',
29106
29107     
29108     undoManager : false,
29109     /**
29110      * Protected method that will not generally be called directly. It
29111      * is called when the editor initializes the iframe with HTML contents. Override this method if you
29112      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29113      */
29114     getDocMarkup : function(){
29115         // body styles..
29116         var st = '';
29117         
29118         // inherit styels from page...?? 
29119         if (this.stylesheets === false) {
29120             
29121             Roo.get(document.head).select('style').each(function(node) {
29122                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29123             });
29124             
29125             Roo.get(document.head).select('link').each(function(node) { 
29126                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29127             });
29128             
29129         } else if (!this.stylesheets.length) {
29130                 // simple..
29131                 st = '<style type="text/css">' +
29132                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29133                    '</style>';
29134         } else {
29135             for (var i in this.stylesheets) {
29136                 if (typeof(this.stylesheets[i]) != 'string') {
29137                     continue;
29138                 }
29139                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29140             }
29141             
29142         }
29143         
29144         st +=  '<style type="text/css">' +
29145             'IMG { cursor: pointer } ' +
29146         '</style>';
29147         
29148         st += '<meta name="google" content="notranslate">';
29149         
29150         var cls = 'notranslate roo-htmleditor-body';
29151         
29152         if(this.bodyCls.length){
29153             cls += ' ' + this.bodyCls;
29154         }
29155         
29156         return '<html  class="notranslate" translate="no"><head>' + st  +
29157             //<style type="text/css">' +
29158             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29159             //'</style>' +
29160             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
29161     },
29162
29163     // private
29164     onRender : function(ct, position)
29165     {
29166         var _t = this;
29167         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29168         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29169         
29170         
29171         this.el.dom.style.border = '0 none';
29172         this.el.dom.setAttribute('tabIndex', -1);
29173         this.el.addClass('x-hidden hide');
29174         
29175         
29176         
29177         if(Roo.isIE){ // fix IE 1px bogus margin
29178             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29179         }
29180        
29181         
29182         this.frameId = Roo.id();
29183         
29184          
29185         
29186         var iframe = this.owner.wrap.createChild({
29187             tag: 'iframe',
29188             cls: 'form-control', // bootstrap..
29189             id: this.frameId,
29190             name: this.frameId,
29191             frameBorder : 'no',
29192             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29193         }, this.el
29194         );
29195         
29196         
29197         this.iframe = iframe.dom;
29198
29199         this.assignDocWin();
29200         
29201         this.doc.designMode = 'on';
29202        
29203         this.doc.open();
29204         this.doc.write(this.getDocMarkup());
29205         this.doc.close();
29206
29207         
29208         var task = { // must defer to wait for browser to be ready
29209             run : function(){
29210                 //console.log("run task?" + this.doc.readyState);
29211                 this.assignDocWin();
29212                 if(this.doc.body || this.doc.readyState == 'complete'){
29213                     try {
29214                         this.doc.designMode="on";
29215                         
29216                     } catch (e) {
29217                         return;
29218                     }
29219                     Roo.TaskMgr.stop(task);
29220                     this.initEditor.defer(10, this);
29221                 }
29222             },
29223             interval : 10,
29224             duration: 10000,
29225             scope: this
29226         };
29227         Roo.TaskMgr.start(task);
29228
29229     },
29230
29231     // private
29232     onResize : function(w, h)
29233     {
29234          Roo.log('resize: ' +w + ',' + h );
29235         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29236         if(!this.iframe){
29237             return;
29238         }
29239         if(typeof w == 'number'){
29240             
29241             this.iframe.style.width = w + 'px';
29242         }
29243         if(typeof h == 'number'){
29244             
29245             this.iframe.style.height = h + 'px';
29246             if(this.doc){
29247                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29248             }
29249         }
29250         
29251     },
29252
29253     /**
29254      * Toggles the editor between standard and source edit mode.
29255      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29256      */
29257     toggleSourceEdit : function(sourceEditMode){
29258         
29259         this.sourceEditMode = sourceEditMode === true;
29260         
29261         if(this.sourceEditMode){
29262  
29263             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29264             
29265         }else{
29266             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29267             //this.iframe.className = '';
29268             this.deferFocus();
29269         }
29270         //this.setSize(this.owner.wrap.getSize());
29271         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29272     },
29273
29274     
29275   
29276
29277     /**
29278      * Protected method that will not generally be called directly. If you need/want
29279      * custom HTML cleanup, this is the method you should override.
29280      * @param {String} html The HTML to be cleaned
29281      * return {String} The cleaned HTML
29282      */
29283     cleanHtml : function(html)
29284     {
29285         html = String(html);
29286         if(html.length > 5){
29287             if(Roo.isSafari){ // strip safari nonsense
29288                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29289             }
29290         }
29291         if(html == '&nbsp;'){
29292             html = '';
29293         }
29294         return html;
29295     },
29296
29297     /**
29298      * HTML Editor -> Textarea
29299      * Protected method that will not generally be called directly. Syncs the contents
29300      * of the editor iframe with the textarea.
29301      */
29302     syncValue : function()
29303     {
29304         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29305         if(this.initialized){
29306             
29307             if (this.undoManager) {
29308                 this.undoManager.addEvent();
29309             }
29310
29311             
29312             var bd = (this.doc.body || this.doc.documentElement);
29313            
29314             
29315             var sel = this.win.getSelection();
29316             
29317             var div = document.createElement('div');
29318             div.innerHTML = bd.innerHTML;
29319             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29320             if (gtx.length > 0) {
29321                 var rm = gtx.item(0).parentNode;
29322                 rm.parentNode.removeChild(rm);
29323             }
29324             
29325            
29326             if (this.enableBlocks) {
29327                 new Roo.htmleditor.FilterBlock({ node : div });
29328             }
29329             
29330             var html = div.innerHTML;
29331             
29332             //?? tidy?
29333             if (this.autoClean) {
29334                 
29335                 new Roo.htmleditor.FilterAttributes({
29336                     node : div,
29337                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29338                     attrib_clean : ['href', 'src' ] 
29339                 });
29340                 
29341                 var tidy = new Roo.htmleditor.TidySerializer({
29342                     inner:  true
29343                 });
29344                 html  = tidy.serialize(div);
29345                 
29346             }
29347             
29348             
29349             if(Roo.isSafari){
29350                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29351                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29352                 if(m && m[1]){
29353                     html = '<div style="'+m[0]+'">' + html + '</div>';
29354                 }
29355             }
29356             html = this.cleanHtml(html);
29357             // fix up the special chars.. normaly like back quotes in word...
29358             // however we do not want to do this with chinese..
29359             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29360                 
29361                 var cc = match.charCodeAt();
29362
29363                 // Get the character value, handling surrogate pairs
29364                 if (match.length == 2) {
29365                     // It's a surrogate pair, calculate the Unicode code point
29366                     var high = match.charCodeAt(0) - 0xD800;
29367                     var low  = match.charCodeAt(1) - 0xDC00;
29368                     cc = (high * 0x400) + low + 0x10000;
29369                 }  else if (
29370                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29371                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29372                     (cc >= 0xf900 && cc < 0xfb00 )
29373                 ) {
29374                         return match;
29375                 }  
29376          
29377                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29378                 return "&#" + cc + ";";
29379                 
29380                 
29381             });
29382             
29383             
29384              
29385             if(this.owner.fireEvent('beforesync', this, html) !== false){
29386                 this.el.dom.value = html;
29387                 this.owner.fireEvent('sync', this, html);
29388             }
29389         }
29390     },
29391
29392     /**
29393      * TEXTAREA -> EDITABLE
29394      * Protected method that will not generally be called directly. Pushes the value of the textarea
29395      * into the iframe editor.
29396      */
29397     pushValue : function()
29398     {
29399         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29400         if(this.initialized){
29401             var v = this.el.dom.value.trim();
29402             
29403             
29404             if(this.owner.fireEvent('beforepush', this, v) !== false){
29405                 var d = (this.doc.body || this.doc.documentElement);
29406                 d.innerHTML = v;
29407                  
29408                 this.el.dom.value = d.innerHTML;
29409                 this.owner.fireEvent('push', this, v);
29410             }
29411             if (this.autoClean) {
29412                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29413                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29414             }
29415             if (this.enableBlocks) {
29416                 Roo.htmleditor.Block.initAll(this.doc.body);
29417             }
29418             
29419             this.updateLanguage();
29420             
29421             var lc = this.doc.body.lastChild;
29422             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29423                 // add an extra line at the end.
29424                 this.doc.body.appendChild(this.doc.createElement('br'));
29425             }
29426             
29427             
29428         }
29429     },
29430
29431     // private
29432     deferFocus : function(){
29433         this.focus.defer(10, this);
29434     },
29435
29436     // doc'ed in Field
29437     focus : function(){
29438         if(this.win && !this.sourceEditMode){
29439             this.win.focus();
29440         }else{
29441             this.el.focus();
29442         }
29443     },
29444     
29445     assignDocWin: function()
29446     {
29447         var iframe = this.iframe;
29448         
29449          if(Roo.isIE){
29450             this.doc = iframe.contentWindow.document;
29451             this.win = iframe.contentWindow;
29452         } else {
29453 //            if (!Roo.get(this.frameId)) {
29454 //                return;
29455 //            }
29456 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29457 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29458             
29459             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29460                 return;
29461             }
29462             
29463             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29464             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29465         }
29466     },
29467     
29468     // private
29469     initEditor : function(){
29470         //console.log("INIT EDITOR");
29471         this.assignDocWin();
29472         
29473         
29474         
29475         this.doc.designMode="on";
29476         this.doc.open();
29477         this.doc.write(this.getDocMarkup());
29478         this.doc.close();
29479         
29480         var dbody = (this.doc.body || this.doc.documentElement);
29481         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29482         // this copies styles from the containing element into thsi one..
29483         // not sure why we need all of this..
29484         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29485         
29486         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29487         //ss['background-attachment'] = 'fixed'; // w3c
29488         dbody.bgProperties = 'fixed'; // ie
29489         dbody.setAttribute("translate", "no");
29490         
29491         //Roo.DomHelper.applyStyles(dbody, ss);
29492         Roo.EventManager.on(this.doc, {
29493              
29494             'mouseup': this.onEditorEvent,
29495             'dblclick': this.onEditorEvent,
29496             'click': this.onEditorEvent,
29497             'keyup': this.onEditorEvent,
29498             
29499             buffer:100,
29500             scope: this
29501         });
29502         Roo.EventManager.on(this.doc, {
29503             'paste': this.onPasteEvent,
29504             scope : this
29505         });
29506         if(Roo.isGecko){
29507             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29508         }
29509         //??? needed???
29510         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29511             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29512         }
29513         this.initialized = true;
29514
29515         
29516         // initialize special key events - enter
29517         new Roo.htmleditor.KeyEnter({core : this});
29518         
29519          
29520         
29521         this.owner.fireEvent('initialize', this);
29522         this.pushValue();
29523     },
29524     // this is to prevent a href clicks resulting in a redirect?
29525    
29526     onPasteEvent : function(e,v)
29527     {
29528         // I think we better assume paste is going to be a dirty load of rubish from word..
29529         
29530         // even pasting into a 'email version' of this widget will have to clean up that mess.
29531         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29532         
29533         // check what type of paste - if it's an image, then handle it differently.
29534         if (cd.files && cd.files.length > 0) {
29535             // pasting images?
29536             var urlAPI = (window.createObjectURL && window) || 
29537                 (window.URL && URL.revokeObjectURL && URL) || 
29538                 (window.webkitURL && webkitURL);
29539     
29540             var url = urlAPI.createObjectURL( cd.files[0]);
29541             this.insertAtCursor('<img src=" + url + ">');
29542             return false;
29543         }
29544         if (cd.types.indexOf('text/html') < 0 ) {
29545             return false;
29546         }
29547         var images = [];
29548         var html = cd.getData('text/html'); // clipboard event
29549         if (cd.types.indexOf('text/rtf') > -1) {
29550             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29551             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29552         }
29553         //Roo.log(images);
29554         //Roo.log(imgs);
29555         // fixme..
29556         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29557                        .map(function(g) { return g.toDataURL(); })
29558                        .filter(function(g) { return g != 'about:blank'; });
29559         
29560         //Roo.log(html);
29561         html = this.cleanWordChars(html);
29562         
29563         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29564         
29565         
29566         var sn = this.getParentElement();
29567         // check if d contains a table, and prevent nesting??
29568         //Roo.log(d.getElementsByTagName('table'));
29569         //Roo.log(sn);
29570         //Roo.log(sn.closest('table'));
29571         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29572             e.preventDefault();
29573             this.insertAtCursor("You can not nest tables");
29574             //Roo.log("prevent?"); // fixme - 
29575             return false;
29576         }
29577         
29578         
29579         
29580         if (images.length > 0) {
29581             // replace all v:imagedata - with img.
29582             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
29583             Roo.each(ar, function(node) {
29584                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
29585                 node.parentNode.removeChild(node);
29586             });
29587             
29588             
29589             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29590                 img.setAttribute('src', images[i]);
29591             });
29592         }
29593         if (this.autoClean) {
29594             new Roo.htmleditor.FilterWord({ node : d });
29595             
29596             new Roo.htmleditor.FilterStyleToTag({ node : d });
29597             new Roo.htmleditor.FilterAttributes({
29598                 node : d,
29599                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29600                 attrib_clean : ['href', 'src' ] 
29601             });
29602             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29603             // should be fonts..
29604             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
29605             new Roo.htmleditor.FilterParagraph({ node : d });
29606             new Roo.htmleditor.FilterSpan({ node : d });
29607             new Roo.htmleditor.FilterLongBr({ node : d });
29608             new Roo.htmleditor.FilterComment({ node : d });
29609             
29610             
29611         }
29612         if (this.enableBlocks) {
29613                 
29614             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29615                 if (img.closest('figure')) { // assume!! that it's aready
29616                     return;
29617                 }
29618                 var fig  = new Roo.htmleditor.BlockFigure({
29619                     image_src  : img.src
29620                 });
29621                 fig.updateElement(img); // replace it..
29622                 
29623             });
29624         }
29625         
29626         
29627         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29628         if (this.enableBlocks) {
29629             Roo.htmleditor.Block.initAll(this.doc.body);
29630         }
29631          
29632         
29633         e.preventDefault();
29634         return false;
29635         // default behaveiour should be our local cleanup paste? (optional?)
29636         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29637         //this.owner.fireEvent('paste', e, v);
29638     },
29639     // private
29640     onDestroy : function(){
29641         
29642         
29643         
29644         if(this.rendered){
29645             
29646             //for (var i =0; i < this.toolbars.length;i++) {
29647             //    // fixme - ask toolbars for heights?
29648             //    this.toolbars[i].onDestroy();
29649            // }
29650             
29651             //this.wrap.dom.innerHTML = '';
29652             //this.wrap.remove();
29653         }
29654     },
29655
29656     // private
29657     onFirstFocus : function(){
29658         
29659         this.assignDocWin();
29660         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29661         
29662         this.activated = true;
29663          
29664     
29665         if(Roo.isGecko){ // prevent silly gecko errors
29666             this.win.focus();
29667             var s = this.win.getSelection();
29668             if(!s.focusNode || s.focusNode.nodeType != 3){
29669                 var r = s.getRangeAt(0);
29670                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29671                 r.collapse(true);
29672                 this.deferFocus();
29673             }
29674             try{
29675                 this.execCmd('useCSS', true);
29676                 this.execCmd('styleWithCSS', false);
29677             }catch(e){}
29678         }
29679         this.owner.fireEvent('activate', this);
29680     },
29681
29682     // private
29683     adjustFont: function(btn){
29684         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29685         //if(Roo.isSafari){ // safari
29686         //    adjust *= 2;
29687        // }
29688         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29689         if(Roo.isSafari){ // safari
29690             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29691             v =  (v < 10) ? 10 : v;
29692             v =  (v > 48) ? 48 : v;
29693             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29694             
29695         }
29696         
29697         
29698         v = Math.max(1, v+adjust);
29699         
29700         this.execCmd('FontSize', v  );
29701     },
29702
29703     onEditorEvent : function(e)
29704     {
29705          
29706         
29707         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29708             return; // we do not handle this.. (undo manager does..)
29709         }
29710         // in theory this detects if the last element is not a br, then we try and do that.
29711         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29712         if (e &&
29713             e.target.nodeName == 'BODY' &&
29714             e.type == "mouseup" &&
29715             this.doc.body.lastChild
29716            ) {
29717             var lc = this.doc.body.lastChild;
29718             // gtx-trans is google translate plugin adding crap.
29719             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29720                 lc = lc.previousSibling;
29721             }
29722             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29723             // if last element is <BR> - then dont do anything.
29724             
29725                 var ns = this.doc.createElement('br');
29726                 this.doc.body.appendChild(ns);
29727                 range = this.doc.createRange();
29728                 range.setStartAfter(ns);
29729                 range.collapse(true);
29730                 var sel = this.win.getSelection();
29731                 sel.removeAllRanges();
29732                 sel.addRange(range);
29733             }
29734         }
29735         
29736         
29737         
29738         this.fireEditorEvent(e);
29739       //  this.updateToolbar();
29740         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29741     },
29742     
29743     fireEditorEvent: function(e)
29744     {
29745         this.owner.fireEvent('editorevent', this, e);
29746     },
29747
29748     insertTag : function(tg)
29749     {
29750         // could be a bit smarter... -> wrap the current selected tRoo..
29751         if (tg.toLowerCase() == 'span' ||
29752             tg.toLowerCase() == 'code' ||
29753             tg.toLowerCase() == 'sup' ||
29754             tg.toLowerCase() == 'sub' 
29755             ) {
29756             
29757             range = this.createRange(this.getSelection());
29758             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29759             wrappingNode.appendChild(range.extractContents());
29760             range.insertNode(wrappingNode);
29761
29762             return;
29763             
29764             
29765             
29766         }
29767         this.execCmd("formatblock",   tg);
29768         this.undoManager.addEvent(); 
29769     },
29770     
29771     insertText : function(txt)
29772     {
29773         
29774         
29775         var range = this.createRange();
29776         range.deleteContents();
29777                //alert(Sender.getAttribute('label'));
29778                
29779         range.insertNode(this.doc.createTextNode(txt));
29780         this.undoManager.addEvent();
29781     } ,
29782     
29783      
29784
29785     /**
29786      * Executes a Midas editor command on the editor document and performs necessary focus and
29787      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29788      * @param {String} cmd The Midas command
29789      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29790      */
29791     relayCmd : function(cmd, value)
29792     {
29793         
29794         switch (cmd) {
29795             case 'justifyleft':
29796             case 'justifyright':
29797             case 'justifycenter':
29798                 // if we are in a cell, then we will adjust the
29799                 var n = this.getParentElement();
29800                 var td = n.closest('td');
29801                 if (td) {
29802                     var bl = Roo.htmleditor.Block.factory(td);
29803                     bl.textAlign = cmd.replace('justify','');
29804                     bl.updateElement();
29805                     this.owner.fireEvent('editorevent', this);
29806                     return;
29807                 }
29808                 this.execCmd('styleWithCSS', true); // 
29809                 break;
29810             case 'bold':
29811             case 'italic':
29812                 // if there is no selection, then we insert, and set the curson inside it..
29813                 this.execCmd('styleWithCSS', false); 
29814                 break;
29815                 
29816         
29817             default:
29818                 break;
29819         }
29820         
29821         
29822         this.win.focus();
29823         this.execCmd(cmd, value);
29824         this.owner.fireEvent('editorevent', this);
29825         //this.updateToolbar();
29826         this.owner.deferFocus();
29827     },
29828
29829     /**
29830      * Executes a Midas editor command directly on the editor document.
29831      * For visual commands, you should use {@link #relayCmd} instead.
29832      * <b>This should only be called after the editor is initialized.</b>
29833      * @param {String} cmd The Midas command
29834      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29835      */
29836     execCmd : function(cmd, value){
29837         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29838         this.syncValue();
29839     },
29840  
29841  
29842    
29843     /**
29844      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29845      * to insert tRoo.
29846      * @param {String} text | dom node.. 
29847      */
29848     insertAtCursor : function(text)
29849     {
29850         
29851         if(!this.activated){
29852             return;
29853         }
29854          
29855         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29856             this.win.focus();
29857             
29858             
29859             // from jquery ui (MIT licenced)
29860             var range, node;
29861             var win = this.win;
29862             
29863             if (win.getSelection && win.getSelection().getRangeAt) {
29864                 
29865                 // delete the existing?
29866                 
29867                 this.createRange(this.getSelection()).deleteContents();
29868                 range = win.getSelection().getRangeAt(0);
29869                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29870                 range.insertNode(node);
29871                 range = range.cloneRange();
29872                 range.collapse(false);
29873                  
29874                 win.getSelection().removeAllRanges();
29875                 win.getSelection().addRange(range);
29876                 
29877                 
29878                 
29879             } else if (win.document.selection && win.document.selection.createRange) {
29880                 // no firefox support
29881                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29882                 win.document.selection.createRange().pasteHTML(txt);
29883             
29884             } else {
29885                 // no firefox support
29886                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29887                 this.execCmd('InsertHTML', txt);
29888             } 
29889             this.syncValue();
29890             
29891             this.deferFocus();
29892         }
29893     },
29894  // private
29895     mozKeyPress : function(e){
29896         if(e.ctrlKey){
29897             var c = e.getCharCode(), cmd;
29898           
29899             if(c > 0){
29900                 c = String.fromCharCode(c).toLowerCase();
29901                 switch(c){
29902                     case 'b':
29903                         cmd = 'bold';
29904                         break;
29905                     case 'i':
29906                         cmd = 'italic';
29907                         break;
29908                     
29909                     case 'u':
29910                         cmd = 'underline';
29911                         break;
29912                     
29913                     //case 'v':
29914                       //  this.cleanUpPaste.defer(100, this);
29915                       //  return;
29916                         
29917                 }
29918                 if(cmd){
29919                     
29920                     this.relayCmd(cmd);
29921                     //this.win.focus();
29922                     //this.execCmd(cmd);
29923                     //this.deferFocus();
29924                     e.preventDefault();
29925                 }
29926                 
29927             }
29928         }
29929     },
29930
29931     // private
29932     fixKeys : function(){ // load time branching for fastest keydown performance
29933         
29934         
29935         if(Roo.isIE){
29936             return function(e){
29937                 var k = e.getKey(), r;
29938                 if(k == e.TAB){
29939                     e.stopEvent();
29940                     r = this.doc.selection.createRange();
29941                     if(r){
29942                         r.collapse(true);
29943                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29944                         this.deferFocus();
29945                     }
29946                     return;
29947                 }
29948                 /// this is handled by Roo.htmleditor.KeyEnter
29949                  /*
29950                 if(k == e.ENTER){
29951                     r = this.doc.selection.createRange();
29952                     if(r){
29953                         var target = r.parentElement();
29954                         if(!target || target.tagName.toLowerCase() != 'li'){
29955                             e.stopEvent();
29956                             r.pasteHTML('<br/>');
29957                             r.collapse(false);
29958                             r.select();
29959                         }
29960                     }
29961                 }
29962                 */
29963                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29964                 //    this.cleanUpPaste.defer(100, this);
29965                 //    return;
29966                 //}
29967                 
29968                 
29969             };
29970         }else if(Roo.isOpera){
29971             return function(e){
29972                 var k = e.getKey();
29973                 if(k == e.TAB){
29974                     e.stopEvent();
29975                     this.win.focus();
29976                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29977                     this.deferFocus();
29978                 }
29979                
29980                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29981                 //    this.cleanUpPaste.defer(100, this);
29982                  //   return;
29983                 //}
29984                 
29985             };
29986         }else if(Roo.isSafari){
29987             return function(e){
29988                 var k = e.getKey();
29989                 
29990                 if(k == e.TAB){
29991                     e.stopEvent();
29992                     this.execCmd('InsertText','\t');
29993                     this.deferFocus();
29994                     return;
29995                 }
29996                  this.mozKeyPress(e);
29997                 
29998                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29999                  //   this.cleanUpPaste.defer(100, this);
30000                  //   return;
30001                // }
30002                 
30003              };
30004         }
30005     }(),
30006     
30007     getAllAncestors: function()
30008     {
30009         var p = this.getSelectedNode();
30010         var a = [];
30011         if (!p) {
30012             a.push(p); // push blank onto stack..
30013             p = this.getParentElement();
30014         }
30015         
30016         
30017         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30018             a.push(p);
30019             p = p.parentNode;
30020         }
30021         a.push(this.doc.body);
30022         return a;
30023     },
30024     lastSel : false,
30025     lastSelNode : false,
30026     
30027     
30028     getSelection : function() 
30029     {
30030         this.assignDocWin();
30031         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30032     },
30033     /**
30034      * Select a dom node
30035      * @param {DomElement} node the node to select
30036      */
30037     selectNode : function(node, collapse)
30038     {
30039         var nodeRange = node.ownerDocument.createRange();
30040         try {
30041             nodeRange.selectNode(node);
30042         } catch (e) {
30043             nodeRange.selectNodeContents(node);
30044         }
30045         if (collapse === true) {
30046             nodeRange.collapse(true);
30047         }
30048         //
30049         var s = this.win.getSelection();
30050         s.removeAllRanges();
30051         s.addRange(nodeRange);
30052     },
30053     
30054     getSelectedNode: function() 
30055     {
30056         // this may only work on Gecko!!!
30057         
30058         // should we cache this!!!!
30059         
30060          
30061          
30062         var range = this.createRange(this.getSelection()).cloneRange();
30063         
30064         if (Roo.isIE) {
30065             var parent = range.parentElement();
30066             while (true) {
30067                 var testRange = range.duplicate();
30068                 testRange.moveToElementText(parent);
30069                 if (testRange.inRange(range)) {
30070                     break;
30071                 }
30072                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30073                     break;
30074                 }
30075                 parent = parent.parentElement;
30076             }
30077             return parent;
30078         }
30079         
30080         // is ancestor a text element.
30081         var ac =  range.commonAncestorContainer;
30082         if (ac.nodeType == 3) {
30083             ac = ac.parentNode;
30084         }
30085         
30086         var ar = ac.childNodes;
30087          
30088         var nodes = [];
30089         var other_nodes = [];
30090         var has_other_nodes = false;
30091         for (var i=0;i<ar.length;i++) {
30092             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
30093                 continue;
30094             }
30095             // fullly contained node.
30096             
30097             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30098                 nodes.push(ar[i]);
30099                 continue;
30100             }
30101             
30102             // probably selected..
30103             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30104                 other_nodes.push(ar[i]);
30105                 continue;
30106             }
30107             // outer..
30108             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
30109                 continue;
30110             }
30111             
30112             
30113             has_other_nodes = true;
30114         }
30115         if (!nodes.length && other_nodes.length) {
30116             nodes= other_nodes;
30117         }
30118         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30119             return false;
30120         }
30121         
30122         return nodes[0];
30123     },
30124     
30125     
30126     createRange: function(sel)
30127     {
30128         // this has strange effects when using with 
30129         // top toolbar - not sure if it's a great idea.
30130         //this.editor.contentWindow.focus();
30131         if (typeof sel != "undefined") {
30132             try {
30133                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30134             } catch(e) {
30135                 return this.doc.createRange();
30136             }
30137         } else {
30138             return this.doc.createRange();
30139         }
30140     },
30141     getParentElement: function()
30142     {
30143         
30144         this.assignDocWin();
30145         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30146         
30147         var range = this.createRange(sel);
30148          
30149         try {
30150             var p = range.commonAncestorContainer;
30151             while (p.nodeType == 3) { // text node
30152                 p = p.parentNode;
30153             }
30154             return p;
30155         } catch (e) {
30156             return null;
30157         }
30158     
30159     },
30160     /***
30161      *
30162      * Range intersection.. the hard stuff...
30163      *  '-1' = before
30164      *  '0' = hits..
30165      *  '1' = after.
30166      *         [ -- selected range --- ]
30167      *   [fail]                        [fail]
30168      *
30169      *    basically..
30170      *      if end is before start or  hits it. fail.
30171      *      if start is after end or hits it fail.
30172      *
30173      *   if either hits (but other is outside. - then it's not 
30174      *   
30175      *    
30176      **/
30177     
30178     
30179     // @see http://www.thismuchiknow.co.uk/?p=64.
30180     rangeIntersectsNode : function(range, node)
30181     {
30182         var nodeRange = node.ownerDocument.createRange();
30183         try {
30184             nodeRange.selectNode(node);
30185         } catch (e) {
30186             nodeRange.selectNodeContents(node);
30187         }
30188     
30189         var rangeStartRange = range.cloneRange();
30190         rangeStartRange.collapse(true);
30191     
30192         var rangeEndRange = range.cloneRange();
30193         rangeEndRange.collapse(false);
30194     
30195         var nodeStartRange = nodeRange.cloneRange();
30196         nodeStartRange.collapse(true);
30197     
30198         var nodeEndRange = nodeRange.cloneRange();
30199         nodeEndRange.collapse(false);
30200     
30201         return rangeStartRange.compareBoundaryPoints(
30202                  Range.START_TO_START, nodeEndRange) == -1 &&
30203                rangeEndRange.compareBoundaryPoints(
30204                  Range.START_TO_START, nodeStartRange) == 1;
30205         
30206          
30207     },
30208     rangeCompareNode : function(range, node)
30209     {
30210         var nodeRange = node.ownerDocument.createRange();
30211         try {
30212             nodeRange.selectNode(node);
30213         } catch (e) {
30214             nodeRange.selectNodeContents(node);
30215         }
30216         
30217         
30218         range.collapse(true);
30219     
30220         nodeRange.collapse(true);
30221      
30222         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30223         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30224          
30225         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30226         
30227         var nodeIsBefore   =  ss == 1;
30228         var nodeIsAfter    = ee == -1;
30229         
30230         if (nodeIsBefore && nodeIsAfter) {
30231             return 0; // outer
30232         }
30233         if (!nodeIsBefore && nodeIsAfter) {
30234             return 1; //right trailed.
30235         }
30236         
30237         if (nodeIsBefore && !nodeIsAfter) {
30238             return 2;  // left trailed.
30239         }
30240         // fully contined.
30241         return 3;
30242     },
30243  
30244     cleanWordChars : function(input) {// change the chars to hex code
30245         
30246        var swapCodes  = [ 
30247             [    8211, "&#8211;" ], 
30248             [    8212, "&#8212;" ], 
30249             [    8216,  "'" ],  
30250             [    8217, "'" ],  
30251             [    8220, '"' ],  
30252             [    8221, '"' ],  
30253             [    8226, "*" ],  
30254             [    8230, "..." ]
30255         ]; 
30256         var output = input;
30257         Roo.each(swapCodes, function(sw) { 
30258             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30259             
30260             output = output.replace(swapper, sw[1]);
30261         });
30262         
30263         return output;
30264     },
30265     
30266      
30267     
30268         
30269     
30270     cleanUpChild : function (node)
30271     {
30272         
30273         new Roo.htmleditor.FilterComment({node : node});
30274         new Roo.htmleditor.FilterAttributes({
30275                 node : node,
30276                 attrib_black : this.ablack,
30277                 attrib_clean : this.aclean,
30278                 style_white : this.cwhite,
30279                 style_black : this.cblack
30280         });
30281         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30282         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30283          
30284         
30285     },
30286     
30287     /**
30288      * Clean up MS wordisms...
30289      * @deprecated - use filter directly
30290      */
30291     cleanWord : function(node)
30292     {
30293         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30294         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30295         
30296     },
30297    
30298     
30299     /**
30300
30301      * @deprecated - use filters
30302      */
30303     cleanTableWidths : function(node)
30304     {
30305         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30306         
30307  
30308     },
30309     
30310      
30311         
30312     applyBlacklists : function()
30313     {
30314         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30315         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30316         
30317         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30318         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30319         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30320         
30321         this.white = [];
30322         this.black = [];
30323         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30324             if (b.indexOf(tag) > -1) {
30325                 return;
30326             }
30327             this.white.push(tag);
30328             
30329         }, this);
30330         
30331         Roo.each(w, function(tag) {
30332             if (b.indexOf(tag) > -1) {
30333                 return;
30334             }
30335             if (this.white.indexOf(tag) > -1) {
30336                 return;
30337             }
30338             this.white.push(tag);
30339             
30340         }, this);
30341         
30342         
30343         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30344             if (w.indexOf(tag) > -1) {
30345                 return;
30346             }
30347             this.black.push(tag);
30348             
30349         }, this);
30350         
30351         Roo.each(b, function(tag) {
30352             if (w.indexOf(tag) > -1) {
30353                 return;
30354             }
30355             if (this.black.indexOf(tag) > -1) {
30356                 return;
30357             }
30358             this.black.push(tag);
30359             
30360         }, this);
30361         
30362         
30363         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30364         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30365         
30366         this.cwhite = [];
30367         this.cblack = [];
30368         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30369             if (b.indexOf(tag) > -1) {
30370                 return;
30371             }
30372             this.cwhite.push(tag);
30373             
30374         }, this);
30375         
30376         Roo.each(w, function(tag) {
30377             if (b.indexOf(tag) > -1) {
30378                 return;
30379             }
30380             if (this.cwhite.indexOf(tag) > -1) {
30381                 return;
30382             }
30383             this.cwhite.push(tag);
30384             
30385         }, this);
30386         
30387         
30388         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30389             if (w.indexOf(tag) > -1) {
30390                 return;
30391             }
30392             this.cblack.push(tag);
30393             
30394         }, this);
30395         
30396         Roo.each(b, function(tag) {
30397             if (w.indexOf(tag) > -1) {
30398                 return;
30399             }
30400             if (this.cblack.indexOf(tag) > -1) {
30401                 return;
30402             }
30403             this.cblack.push(tag);
30404             
30405         }, this);
30406     },
30407     
30408     setStylesheets : function(stylesheets)
30409     {
30410         if(typeof(stylesheets) == 'string'){
30411             Roo.get(this.iframe.contentDocument.head).createChild({
30412                 tag : 'link',
30413                 rel : 'stylesheet',
30414                 type : 'text/css',
30415                 href : stylesheets
30416             });
30417             
30418             return;
30419         }
30420         var _this = this;
30421      
30422         Roo.each(stylesheets, function(s) {
30423             if(!s.length){
30424                 return;
30425             }
30426             
30427             Roo.get(_this.iframe.contentDocument.head).createChild({
30428                 tag : 'link',
30429                 rel : 'stylesheet',
30430                 type : 'text/css',
30431                 href : s
30432             });
30433         });
30434
30435         
30436     },
30437     
30438     
30439     updateLanguage : function()
30440     {
30441         if (!this.iframe || !this.iframe.contentDocument) {
30442             return;
30443         }
30444         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30445     },
30446     
30447     
30448     removeStylesheets : function()
30449     {
30450         var _this = this;
30451         
30452         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30453             s.remove();
30454         });
30455     },
30456     
30457     setStyle : function(style)
30458     {
30459         Roo.get(this.iframe.contentDocument.head).createChild({
30460             tag : 'style',
30461             type : 'text/css',
30462             html : style
30463         });
30464
30465         return;
30466     }
30467     
30468     // hide stuff that is not compatible
30469     /**
30470      * @event blur
30471      * @hide
30472      */
30473     /**
30474      * @event change
30475      * @hide
30476      */
30477     /**
30478      * @event focus
30479      * @hide
30480      */
30481     /**
30482      * @event specialkey
30483      * @hide
30484      */
30485     /**
30486      * @cfg {String} fieldClass @hide
30487      */
30488     /**
30489      * @cfg {String} focusClass @hide
30490      */
30491     /**
30492      * @cfg {String} autoCreate @hide
30493      */
30494     /**
30495      * @cfg {String} inputType @hide
30496      */
30497     /**
30498      * @cfg {String} invalidClass @hide
30499      */
30500     /**
30501      * @cfg {String} invalidText @hide
30502      */
30503     /**
30504      * @cfg {String} msgFx @hide
30505      */
30506     /**
30507      * @cfg {String} validateOnBlur @hide
30508      */
30509 });
30510
30511 Roo.HtmlEditorCore.white = [
30512         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30513         
30514        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30515        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30516        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30517        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30518        'TABLE',   'UL',         'XMP', 
30519        
30520        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30521       'THEAD',   'TR', 
30522      
30523       'DIR', 'MENU', 'OL', 'UL', 'DL',
30524        
30525       'EMBED',  'OBJECT'
30526 ];
30527
30528
30529 Roo.HtmlEditorCore.black = [
30530     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30531         'APPLET', // 
30532         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30533         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30534         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30535         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30536         //'FONT' // CLEAN LATER..
30537         'COLGROUP', 'COL'   // messy tables.
30538         
30539         
30540 ];
30541 Roo.HtmlEditorCore.clean = [ // ?? needed???
30542      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30543 ];
30544 Roo.HtmlEditorCore.tag_remove = [
30545     'FONT', 'TBODY'  
30546 ];
30547 // attributes..
30548
30549 Roo.HtmlEditorCore.ablack = [
30550     'on'
30551 ];
30552     
30553 Roo.HtmlEditorCore.aclean = [ 
30554     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30555 ];
30556
30557 // protocols..
30558 Roo.HtmlEditorCore.pwhite= [
30559         'http',  'https',  'mailto'
30560 ];
30561
30562 // white listed style attributes.
30563 Roo.HtmlEditorCore.cwhite= [
30564       //  'text-align', /// default is to allow most things..
30565       
30566          
30567 //        'font-size'//??
30568 ];
30569
30570 // black listed style attributes.
30571 Roo.HtmlEditorCore.cblack= [
30572       //  'font-size' -- this can be set by the project 
30573 ];
30574
30575
30576
30577
30578     /*
30579  * - LGPL
30580  *
30581  * HtmlEditor
30582  * 
30583  */
30584
30585 /**
30586  * @class Roo.bootstrap.form.HtmlEditor
30587  * @extends Roo.bootstrap.form.TextArea
30588  * Bootstrap HtmlEditor class
30589
30590  * @constructor
30591  * Create a new HtmlEditor
30592  * @param {Object} config The config object
30593  */
30594
30595 Roo.bootstrap.form.HtmlEditor = function(config){
30596     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30597     if (!this.toolbars) {
30598         this.toolbars = [];
30599     }
30600     
30601     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30602     this.addEvents({
30603             /**
30604              * @event initialize
30605              * Fires when the editor is fully initialized (including the iframe)
30606              * @param {HtmlEditor} this
30607              */
30608             initialize: true,
30609             /**
30610              * @event activate
30611              * Fires when the editor is first receives the focus. Any insertion must wait
30612              * until after this event.
30613              * @param {HtmlEditor} this
30614              */
30615             activate: true,
30616              /**
30617              * @event beforesync
30618              * Fires before the textarea is updated with content from the editor iframe. Return false
30619              * to cancel the sync.
30620              * @param {HtmlEditor} this
30621              * @param {String} html
30622              */
30623             beforesync: true,
30624              /**
30625              * @event beforepush
30626              * Fires before the iframe editor is updated with content from the textarea. Return false
30627              * to cancel the push.
30628              * @param {HtmlEditor} this
30629              * @param {String} html
30630              */
30631             beforepush: true,
30632              /**
30633              * @event sync
30634              * Fires when the textarea is updated with content from the editor iframe.
30635              * @param {HtmlEditor} this
30636              * @param {String} html
30637              */
30638             sync: true,
30639              /**
30640              * @event push
30641              * Fires when the iframe editor is updated with content from the textarea.
30642              * @param {HtmlEditor} this
30643              * @param {String} html
30644              */
30645             push: true,
30646              /**
30647              * @event editmodechange
30648              * Fires when the editor switches edit modes
30649              * @param {HtmlEditor} this
30650              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30651              */
30652             editmodechange: true,
30653             /**
30654              * @event editorevent
30655              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30656              * @param {HtmlEditor} this
30657              */
30658             editorevent: true,
30659             /**
30660              * @event firstfocus
30661              * Fires when on first focus - needed by toolbars..
30662              * @param {HtmlEditor} this
30663              */
30664             firstfocus: true,
30665             /**
30666              * @event autosave
30667              * Auto save the htmlEditor value as a file into Events
30668              * @param {HtmlEditor} this
30669              */
30670             autosave: true,
30671             /**
30672              * @event savedpreview
30673              * preview the saved version of htmlEditor
30674              * @param {HtmlEditor} this
30675              */
30676             savedpreview: true
30677         });
30678 };
30679
30680
30681 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30682     
30683     
30684       /**
30685      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30686      */
30687     toolbars : false,
30688     
30689      /**
30690     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30691     */
30692     btns : [],
30693    
30694      /**
30695      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30696      *                        Roo.resizable.
30697      */
30698     resizable : false,
30699      /**
30700      * @cfg {Number} height (in pixels)
30701      */   
30702     height: 300,
30703    /**
30704      * @cfg {Number} width (in pixels)
30705      */   
30706     width: false,
30707     
30708     /**
30709      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30710      * 
30711      */
30712     stylesheets: false,
30713     
30714     // id of frame..
30715     frameId: false,
30716     
30717     // private properties
30718     validationEvent : false,
30719     deferHeight: true,
30720     initialized : false,
30721     activated : false,
30722     
30723     onFocus : Roo.emptyFn,
30724     iframePad:3,
30725     hideMode:'offsets',
30726     
30727     tbContainer : false,
30728     
30729     bodyCls : '',
30730     
30731     toolbarContainer :function() {
30732         return this.wrap.select('.x-html-editor-tb',true).first();
30733     },
30734
30735     /**
30736      * Protected method that will not generally be called directly. It
30737      * is called when the editor creates its toolbar. Override this method if you need to
30738      * add custom toolbar buttons.
30739      * @param {HtmlEditor} editor
30740      */
30741     createToolbar : function(){
30742         Roo.log('renewing');
30743         Roo.log("create toolbars");
30744         
30745         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30746         this.toolbars[0].render(this.toolbarContainer());
30747         
30748         return;
30749         
30750 //        if (!editor.toolbars || !editor.toolbars.length) {
30751 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30752 //        }
30753 //        
30754 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30755 //            editor.toolbars[i] = Roo.factory(
30756 //                    typeof(editor.toolbars[i]) == 'string' ?
30757 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30758 //                Roo.bootstrap.form.HtmlEditor);
30759 //            editor.toolbars[i].init(editor);
30760 //        }
30761     },
30762
30763      
30764     // private
30765     onRender : function(ct, position)
30766     {
30767        // Roo.log("Call onRender: " + this.xtype);
30768         var _t = this;
30769         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30770       
30771         this.wrap = this.inputEl().wrap({
30772             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30773         });
30774         
30775         this.editorcore.onRender(ct, position);
30776          
30777         if (this.resizable) {
30778             this.resizeEl = new Roo.Resizable(this.wrap, {
30779                 pinned : true,
30780                 wrap: true,
30781                 dynamic : true,
30782                 minHeight : this.height,
30783                 height: this.height,
30784                 handles : this.resizable,
30785                 width: this.width,
30786                 listeners : {
30787                     resize : function(r, w, h) {
30788                         _t.onResize(w,h); // -something
30789                     }
30790                 }
30791             });
30792             
30793         }
30794         this.createToolbar(this);
30795        
30796         
30797         if(!this.width && this.resizable){
30798             this.setSize(this.wrap.getSize());
30799         }
30800         if (this.resizeEl) {
30801             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30802             // should trigger onReize..
30803         }
30804         
30805     },
30806
30807     // private
30808     onResize : function(w, h)
30809     {
30810         Roo.log('resize: ' +w + ',' + h );
30811         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30812         var ew = false;
30813         var eh = false;
30814         
30815         if(this.inputEl() ){
30816             if(typeof w == 'number'){
30817                 var aw = w - this.wrap.getFrameWidth('lr');
30818                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30819                 ew = aw;
30820             }
30821             if(typeof h == 'number'){
30822                  var tbh = -11;  // fixme it needs to tool bar size!
30823                 for (var i =0; i < this.toolbars.length;i++) {
30824                     // fixme - ask toolbars for heights?
30825                     tbh += this.toolbars[i].el.getHeight();
30826                     //if (this.toolbars[i].footer) {
30827                     //    tbh += this.toolbars[i].footer.el.getHeight();
30828                     //}
30829                 }
30830               
30831                 
30832                 
30833                 
30834                 
30835                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30836                 ah -= 5; // knock a few pixes off for look..
30837                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30838                 var eh = ah;
30839             }
30840         }
30841         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30842         this.editorcore.onResize(ew,eh);
30843         
30844     },
30845
30846     /**
30847      * Toggles the editor between standard and source edit mode.
30848      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30849      */
30850     toggleSourceEdit : function(sourceEditMode)
30851     {
30852         this.editorcore.toggleSourceEdit(sourceEditMode);
30853         
30854         if(this.editorcore.sourceEditMode){
30855             Roo.log('editor - showing textarea');
30856             
30857 //            Roo.log('in');
30858 //            Roo.log(this.syncValue());
30859             this.syncValue();
30860             this.inputEl().removeClass(['hide', 'x-hidden']);
30861             this.inputEl().dom.removeAttribute('tabIndex');
30862             this.inputEl().focus();
30863         }else{
30864             Roo.log('editor - hiding textarea');
30865 //            Roo.log('out')
30866 //            Roo.log(this.pushValue()); 
30867             this.pushValue();
30868             
30869             this.inputEl().addClass(['hide', 'x-hidden']);
30870             this.inputEl().dom.setAttribute('tabIndex', -1);
30871             //this.deferFocus();
30872         }
30873          
30874         if(this.resizable){
30875             this.setSize(this.wrap.getSize());
30876         }
30877         
30878         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30879     },
30880  
30881     // private (for BoxComponent)
30882     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30883
30884     // private (for BoxComponent)
30885     getResizeEl : function(){
30886         return this.wrap;
30887     },
30888
30889     // private (for BoxComponent)
30890     getPositionEl : function(){
30891         return this.wrap;
30892     },
30893
30894     // private
30895     initEvents : function(){
30896         this.originalValue = this.getValue();
30897     },
30898
30899 //    /**
30900 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30901 //     * @method
30902 //     */
30903 //    markInvalid : Roo.emptyFn,
30904 //    /**
30905 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30906 //     * @method
30907 //     */
30908 //    clearInvalid : Roo.emptyFn,
30909
30910     setValue : function(v){
30911         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30912         this.editorcore.pushValue();
30913     },
30914
30915      
30916     // private
30917     deferFocus : function(){
30918         this.focus.defer(10, this);
30919     },
30920
30921     // doc'ed in Field
30922     focus : function(){
30923         this.editorcore.focus();
30924         
30925     },
30926       
30927
30928     // private
30929     onDestroy : function(){
30930         
30931         
30932         
30933         if(this.rendered){
30934             
30935             for (var i =0; i < this.toolbars.length;i++) {
30936                 // fixme - ask toolbars for heights?
30937                 this.toolbars[i].onDestroy();
30938             }
30939             
30940             this.wrap.dom.innerHTML = '';
30941             this.wrap.remove();
30942         }
30943     },
30944
30945     // private
30946     onFirstFocus : function(){
30947         //Roo.log("onFirstFocus");
30948         this.editorcore.onFirstFocus();
30949          for (var i =0; i < this.toolbars.length;i++) {
30950             this.toolbars[i].onFirstFocus();
30951         }
30952         
30953     },
30954     
30955     // private
30956     syncValue : function()
30957     {   
30958         this.editorcore.syncValue();
30959     },
30960     
30961     pushValue : function()
30962     {   
30963         this.editorcore.pushValue();
30964     }
30965      
30966     
30967     // hide stuff that is not compatible
30968     /**
30969      * @event blur
30970      * @hide
30971      */
30972     /**
30973      * @event change
30974      * @hide
30975      */
30976     /**
30977      * @event focus
30978      * @hide
30979      */
30980     /**
30981      * @event specialkey
30982      * @hide
30983      */
30984     /**
30985      * @cfg {String} fieldClass @hide
30986      */
30987     /**
30988      * @cfg {String} focusClass @hide
30989      */
30990     /**
30991      * @cfg {String} autoCreate @hide
30992      */
30993     /**
30994      * @cfg {String} inputType @hide
30995      */
30996      
30997     /**
30998      * @cfg {String} invalidText @hide
30999      */
31000     /**
31001      * @cfg {String} msgFx @hide
31002      */
31003     /**
31004      * @cfg {String} validateOnBlur @hide
31005      */
31006 });
31007  
31008     
31009    
31010    
31011    
31012       
31013 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31014 /**
31015  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31016  * @parent Roo.bootstrap.form.HtmlEditor
31017  * @extends Roo.bootstrap.nav.Simplebar
31018  * Basic Toolbar
31019  * 
31020  * @example
31021  * Usage:
31022  *
31023  new Roo.bootstrap.form.HtmlEditor({
31024     ....
31025     toolbars : [
31026         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31027             disable : { fonts: 1 , format: 1, ..., ... , ...],
31028             btns : [ .... ]
31029         })
31030     }
31031      
31032  * 
31033  * @cfg {Object} disable List of elements to disable..
31034  * @cfg {Array} btns List of additional buttons.
31035  * 
31036  * 
31037  * NEEDS Extra CSS? 
31038  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31039  */
31040  
31041 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31042 {
31043     
31044     Roo.apply(this, config);
31045     
31046     // default disabled, based on 'good practice'..
31047     this.disable = this.disable || {};
31048     Roo.applyIf(this.disable, {
31049         fontSize : true,
31050         colors : true,
31051         specialElements : true
31052     });
31053     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31054     
31055     this.editor = config.editor;
31056     this.editorcore = config.editor.editorcore;
31057     
31058     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31059     
31060     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31061     // dont call parent... till later.
31062 }
31063 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
31064      
31065     bar : true,
31066     
31067     editor : false,
31068     editorcore : false,
31069     
31070     
31071     formats : [
31072         "p" ,  
31073         "h1","h2","h3","h4","h5","h6", 
31074         "pre", "code", 
31075         "abbr", "acronym", "address", "cite", "samp", "var",
31076         'div','span'
31077     ],
31078     
31079     onRender : function(ct, position)
31080     {
31081        // Roo.log("Call onRender: " + this.xtype);
31082         
31083        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31084        Roo.log(this.el);
31085        this.el.dom.style.marginBottom = '0';
31086        var _this = this;
31087        var editorcore = this.editorcore;
31088        var editor= this.editor;
31089        
31090        var children = [];
31091        var btn = function(id,cmd , toggle, handler, html){
31092        
31093             var  event = toggle ? 'toggle' : 'click';
31094        
31095             var a = {
31096                 size : 'sm',
31097                 xtype: 'Button',
31098                 xns: Roo.bootstrap,
31099                 //glyphicon : id,
31100                 fa: id,
31101                 cmd : id || cmd,
31102                 enableToggle:toggle !== false,
31103                 html : html || '',
31104                 pressed : toggle ? false : null,
31105                 listeners : {}
31106             };
31107             a.listeners[toggle ? 'toggle' : 'click'] = function() {
31108                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
31109             };
31110             children.push(a);
31111             return a;
31112        }
31113        
31114     //    var cb_box = function...
31115         
31116         var style = {
31117                 xtype: 'Button',
31118                 size : 'sm',
31119                 xns: Roo.bootstrap,
31120                 fa : 'font',
31121                 //html : 'submit'
31122                 menu : {
31123                     xtype: 'Menu',
31124                     xns: Roo.bootstrap,
31125                     items:  []
31126                 }
31127         };
31128         Roo.each(this.formats, function(f) {
31129             style.menu.items.push({
31130                 xtype :'MenuItem',
31131                 xns: Roo.bootstrap,
31132                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31133                 tagname : f,
31134                 listeners : {
31135                     click : function()
31136                     {
31137                         editorcore.insertTag(this.tagname);
31138                         editor.focus();
31139                     }
31140                 }
31141                 
31142             });
31143         });
31144         children.push(style);   
31145         
31146         btn('bold',false,true);
31147         btn('italic',false,true);
31148         btn('align-left', 'justifyleft',true);
31149         btn('align-center', 'justifycenter',true);
31150         btn('align-right' , 'justifyright',true);
31151         btn('link', false, false, function(btn) {
31152             //Roo.log("create link?");
31153             var url = prompt(this.createLinkText, this.defaultLinkValue);
31154             if(url && url != 'http:/'+'/'){
31155                 this.editorcore.relayCmd('createlink', url);
31156             }
31157         }),
31158         btn('list','insertunorderedlist',true);
31159         btn('pencil', false,true, function(btn){
31160                 Roo.log(this);
31161                 this.toggleSourceEdit(btn.pressed);
31162         });
31163         
31164         if (this.editor.btns.length > 0) {
31165             for (var i = 0; i<this.editor.btns.length; i++) {
31166                 children.push(this.editor.btns[i]);
31167             }
31168         }
31169         
31170         /*
31171         var cog = {
31172                 xtype: 'Button',
31173                 size : 'sm',
31174                 xns: Roo.bootstrap,
31175                 glyphicon : 'cog',
31176                 //html : 'submit'
31177                 menu : {
31178                     xtype: 'Menu',
31179                     xns: Roo.bootstrap,
31180                     items:  []
31181                 }
31182         };
31183         
31184         cog.menu.items.push({
31185             xtype :'MenuItem',
31186             xns: Roo.bootstrap,
31187             html : Clean styles,
31188             tagname : f,
31189             listeners : {
31190                 click : function()
31191                 {
31192                     editorcore.insertTag(this.tagname);
31193                     editor.focus();
31194                 }
31195             }
31196             
31197         });
31198        */
31199         
31200          
31201        this.xtype = 'NavSimplebar';
31202         
31203         for(var i=0;i< children.length;i++) {
31204             
31205             this.buttons.add(this.addxtypeChild(children[i]));
31206             
31207         }
31208         
31209         editor.on('editorevent', this.updateToolbar, this);
31210     },
31211     onBtnClick : function(id)
31212     {
31213        this.editorcore.relayCmd(id);
31214        this.editorcore.focus();
31215     },
31216     
31217     /**
31218      * Protected method that will not generally be called directly. It triggers
31219      * a toolbar update by reading the markup state of the current selection in the editor.
31220      */
31221     updateToolbar: function(){
31222
31223         if(!this.editorcore.activated){
31224             this.editor.onFirstFocus(); // is this neeed?
31225             return;
31226         }
31227
31228         var btns = this.buttons; 
31229         var doc = this.editorcore.doc;
31230         btns.get('bold').setActive(doc.queryCommandState('bold'));
31231         btns.get('italic').setActive(doc.queryCommandState('italic'));
31232         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31233         
31234         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31235         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31236         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31237         
31238         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31239         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31240          /*
31241         
31242         var ans = this.editorcore.getAllAncestors();
31243         if (this.formatCombo) {
31244             
31245             
31246             var store = this.formatCombo.store;
31247             this.formatCombo.setValue("");
31248             for (var i =0; i < ans.length;i++) {
31249                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31250                     // select it..
31251                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31252                     break;
31253                 }
31254             }
31255         }
31256         
31257         
31258         
31259         // hides menus... - so this cant be on a menu...
31260         Roo.bootstrap.MenuMgr.hideAll();
31261         */
31262         Roo.bootstrap.menu.Manager.hideAll();
31263         //this.editorsyncValue();
31264     },
31265     onFirstFocus: function() {
31266         this.buttons.each(function(item){
31267            item.enable();
31268         });
31269     },
31270     toggleSourceEdit : function(sourceEditMode){
31271         
31272           
31273         if(sourceEditMode){
31274             Roo.log("disabling buttons");
31275            this.buttons.each( function(item){
31276                 if(item.cmd != 'pencil'){
31277                     item.disable();
31278                 }
31279             });
31280           
31281         }else{
31282             Roo.log("enabling buttons");
31283             if(this.editorcore.initialized){
31284                 this.buttons.each( function(item){
31285                     item.enable();
31286                 });
31287             }
31288             
31289         }
31290         Roo.log("calling toggole on editor");
31291         // tell the editor that it's been pressed..
31292         this.editor.toggleSourceEdit(sourceEditMode);
31293        
31294     }
31295 });
31296
31297
31298
31299
31300  
31301 /*
31302  * - LGPL
31303  */
31304
31305 /**
31306  * @class Roo.bootstrap.form.Markdown
31307  * @extends Roo.bootstrap.form.TextArea
31308  * Bootstrap Showdown editable area
31309  * @cfg {string} content
31310  * 
31311  * @constructor
31312  * Create a new Showdown
31313  */
31314
31315 Roo.bootstrap.form.Markdown = function(config){
31316     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31317    
31318 };
31319
31320 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31321     
31322     editing :false,
31323     
31324     initEvents : function()
31325     {
31326         
31327         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31328         this.markdownEl = this.el.createChild({
31329             cls : 'roo-markdown-area'
31330         });
31331         this.inputEl().addClass('d-none');
31332         if (this.getValue() == '') {
31333             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31334             
31335         } else {
31336             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31337         }
31338         this.markdownEl.on('click', this.toggleTextEdit, this);
31339         this.on('blur', this.toggleTextEdit, this);
31340         this.on('specialkey', this.resizeTextArea, this);
31341     },
31342     
31343     toggleTextEdit : function()
31344     {
31345         var sh = this.markdownEl.getHeight();
31346         this.inputEl().addClass('d-none');
31347         this.markdownEl.addClass('d-none');
31348         if (!this.editing) {
31349             // show editor?
31350             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31351             this.inputEl().removeClass('d-none');
31352             this.inputEl().focus();
31353             this.editing = true;
31354             return;
31355         }
31356         // show showdown...
31357         this.updateMarkdown();
31358         this.markdownEl.removeClass('d-none');
31359         this.editing = false;
31360         return;
31361     },
31362     updateMarkdown : function()
31363     {
31364         if (this.getValue() == '') {
31365             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31366             return;
31367         }
31368  
31369         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31370     },
31371     
31372     resizeTextArea: function () {
31373         
31374         var sh = 100;
31375         Roo.log([sh, this.getValue().split("\n").length * 30]);
31376         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31377     },
31378     setValue : function(val)
31379     {
31380         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31381         if (!this.editing) {
31382             this.updateMarkdown();
31383         }
31384         
31385     },
31386     focus : function()
31387     {
31388         if (!this.editing) {
31389             this.toggleTextEdit();
31390         }
31391         
31392     }
31393
31394
31395 });/*
31396  * Based on:
31397  * Ext JS Library 1.1.1
31398  * Copyright(c) 2006-2007, Ext JS, LLC.
31399  *
31400  * Originally Released Under LGPL - original licence link has changed is not relivant.
31401  *
31402  * Fork - LGPL
31403  * <script type="text/javascript">
31404  */
31405  
31406 /**
31407  * @class Roo.bootstrap.PagingToolbar
31408  * @extends Roo.bootstrap.nav.Simplebar
31409  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31410  * @constructor
31411  * Create a new PagingToolbar
31412  * @param {Object} config The config object
31413  * @param {Roo.data.Store} store
31414  */
31415 Roo.bootstrap.PagingToolbar = function(config)
31416 {
31417     // old args format still supported... - xtype is prefered..
31418         // created from xtype...
31419     
31420     this.ds = config.dataSource;
31421     
31422     if (config.store && !this.ds) {
31423         this.store= Roo.factory(config.store, Roo.data);
31424         this.ds = this.store;
31425         this.ds.xmodule = this.xmodule || false;
31426     }
31427     
31428     this.toolbarItems = [];
31429     if (config.items) {
31430         this.toolbarItems = config.items;
31431     }
31432     
31433     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31434     
31435     this.cursor = 0;
31436     
31437     if (this.ds) { 
31438         this.bind(this.ds);
31439     }
31440     
31441     if (Roo.bootstrap.version == 4) {
31442         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31443     } else {
31444         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31445     }
31446     
31447 };
31448
31449 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31450     /**
31451      * @cfg {Roo.bootstrap.Button} buttons[]
31452      * Buttons for the toolbar
31453      */
31454      /**
31455      * @cfg {Roo.data.Store} store
31456      * The underlying data store providing the paged data
31457      */
31458     /**
31459      * @cfg {String/HTMLElement/Element} container
31460      * container The id or element that will contain the toolbar
31461      */
31462     /**
31463      * @cfg {Boolean} displayInfo
31464      * True to display the displayMsg (defaults to false)
31465      */
31466     /**
31467      * @cfg {Number} pageSize
31468      * The number of records to display per page (defaults to 20)
31469      */
31470     pageSize: 20,
31471     /**
31472      * @cfg {String} displayMsg
31473      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31474      */
31475     displayMsg : 'Displaying {0} - {1} of {2}',
31476     /**
31477      * @cfg {String} emptyMsg
31478      * The message to display when no records are found (defaults to "No data to display")
31479      */
31480     emptyMsg : 'No data to display',
31481     /**
31482      * Customizable piece of the default paging text (defaults to "Page")
31483      * @type String
31484      */
31485     beforePageText : "Page",
31486     /**
31487      * Customizable piece of the default paging text (defaults to "of %0")
31488      * @type String
31489      */
31490     afterPageText : "of {0}",
31491     /**
31492      * Customizable piece of the default paging text (defaults to "First Page")
31493      * @type String
31494      */
31495     firstText : "First Page",
31496     /**
31497      * Customizable piece of the default paging text (defaults to "Previous Page")
31498      * @type String
31499      */
31500     prevText : "Previous Page",
31501     /**
31502      * Customizable piece of the default paging text (defaults to "Next Page")
31503      * @type String
31504      */
31505     nextText : "Next Page",
31506     /**
31507      * Customizable piece of the default paging text (defaults to "Last Page")
31508      * @type String
31509      */
31510     lastText : "Last Page",
31511     /**
31512      * Customizable piece of the default paging text (defaults to "Refresh")
31513      * @type String
31514      */
31515     refreshText : "Refresh",
31516
31517     buttons : false,
31518     // private
31519     onRender : function(ct, position) 
31520     {
31521         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31522         this.navgroup.parentId = this.id;
31523         this.navgroup.onRender(this.el, null);
31524         // add the buttons to the navgroup
31525         
31526         if(this.displayInfo){
31527             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31528             this.displayEl = this.el.select('.x-paging-info', true).first();
31529 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31530 //            this.displayEl = navel.el.select('span',true).first();
31531         }
31532         
31533         var _this = this;
31534         
31535         if(this.buttons){
31536             Roo.each(_this.buttons, function(e){ // this might need to use render????
31537                Roo.factory(e).render(_this.el);
31538             });
31539         }
31540             
31541         Roo.each(_this.toolbarItems, function(e) {
31542             _this.navgroup.addItem(e);
31543         });
31544         
31545         
31546         this.first = this.navgroup.addItem({
31547             tooltip: this.firstText,
31548             cls: "prev btn-outline-secondary",
31549             html : ' <i class="fa fa-step-backward"></i>',
31550             disabled: true,
31551             preventDefault: true,
31552             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31553         });
31554         
31555         this.prev =  this.navgroup.addItem({
31556             tooltip: this.prevText,
31557             cls: "prev btn-outline-secondary",
31558             html : ' <i class="fa fa-backward"></i>',
31559             disabled: true,
31560             preventDefault: true,
31561             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31562         });
31563     //this.addSeparator();
31564         
31565         
31566         var field = this.navgroup.addItem( {
31567             tagtype : 'span',
31568             cls : 'x-paging-position  btn-outline-secondary',
31569              disabled: true,
31570             html : this.beforePageText  +
31571                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31572                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31573          } ); //?? escaped?
31574         
31575         this.field = field.el.select('input', true).first();
31576         this.field.on("keydown", this.onPagingKeydown, this);
31577         this.field.on("focus", function(){this.dom.select();});
31578     
31579     
31580         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31581         //this.field.setHeight(18);
31582         //this.addSeparator();
31583         this.next = this.navgroup.addItem({
31584             tooltip: this.nextText,
31585             cls: "next btn-outline-secondary",
31586             html : ' <i class="fa fa-forward"></i>',
31587             disabled: true,
31588             preventDefault: true,
31589             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31590         });
31591         this.last = this.navgroup.addItem({
31592             tooltip: this.lastText,
31593             html : ' <i class="fa fa-step-forward"></i>',
31594             cls: "next btn-outline-secondary",
31595             disabled: true,
31596             preventDefault: true,
31597             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31598         });
31599     //this.addSeparator();
31600         this.loading = this.navgroup.addItem({
31601             tooltip: this.refreshText,
31602             cls: "btn-outline-secondary",
31603             html : ' <i class="fa fa-refresh"></i>',
31604             preventDefault: true,
31605             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31606         });
31607         
31608     },
31609
31610     // private
31611     updateInfo : function(){
31612         if(this.displayEl){
31613             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31614             var msg = count == 0 ?
31615                 this.emptyMsg :
31616                 String.format(
31617                     this.displayMsg,
31618                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31619                 );
31620             this.displayEl.update(msg);
31621         }
31622     },
31623
31624     // private
31625     onLoad : function(ds, r, o)
31626     {
31627         this.cursor = o.params && o.params.start ? o.params.start : 0;
31628         
31629         var d = this.getPageData(),
31630             ap = d.activePage,
31631             ps = d.pages;
31632         
31633         
31634         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31635         this.field.dom.value = ap;
31636         this.first.setDisabled(ap == 1);
31637         this.prev.setDisabled(ap == 1);
31638         this.next.setDisabled(ap == ps);
31639         this.last.setDisabled(ap == ps);
31640         this.loading.enable();
31641         this.updateInfo();
31642     },
31643
31644     // private
31645     getPageData : function(){
31646         var total = this.ds.getTotalCount();
31647         return {
31648             total : total,
31649             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31650             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31651         };
31652     },
31653
31654     // private
31655     onLoadError : function(proxy, o){
31656         this.loading.enable();
31657         if (this.ds.events.loadexception.listeners.length  < 2) {
31658             // nothing has been assigned to loadexception except this...
31659             // so 
31660             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31661
31662         }
31663     },
31664
31665     // private
31666     onPagingKeydown : function(e){
31667         var k = e.getKey();
31668         var d = this.getPageData();
31669         if(k == e.RETURN){
31670             var v = this.field.dom.value, pageNum;
31671             if(!v || isNaN(pageNum = parseInt(v, 10))){
31672                 this.field.dom.value = d.activePage;
31673                 return;
31674             }
31675             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31676             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31677             e.stopEvent();
31678         }
31679         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))
31680         {
31681           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31682           this.field.dom.value = pageNum;
31683           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31684           e.stopEvent();
31685         }
31686         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31687         {
31688           var v = this.field.dom.value, pageNum; 
31689           var increment = (e.shiftKey) ? 10 : 1;
31690           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31691                 increment *= -1;
31692           }
31693           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31694             this.field.dom.value = d.activePage;
31695             return;
31696           }
31697           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31698           {
31699             this.field.dom.value = parseInt(v, 10) + increment;
31700             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31701             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31702           }
31703           e.stopEvent();
31704         }
31705     },
31706
31707     // private
31708     beforeLoad : function(){
31709         if(this.loading){
31710             this.loading.disable();
31711         }
31712     },
31713
31714     // private
31715     onClick : function(which){
31716         
31717         var ds = this.ds;
31718         if (!ds) {
31719             return;
31720         }
31721         
31722         switch(which){
31723             case "first":
31724                 ds.load({params:{start: 0, limit: this.pageSize}});
31725             break;
31726             case "prev":
31727                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31728             break;
31729             case "next":
31730                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31731             break;
31732             case "last":
31733                 var total = ds.getTotalCount();
31734                 var extra = total % this.pageSize;
31735                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31736                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31737             break;
31738             case "refresh":
31739                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31740             break;
31741         }
31742     },
31743
31744     /**
31745      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31746      * @param {Roo.data.Store} store The data store to unbind
31747      */
31748     unbind : function(ds){
31749         ds.un("beforeload", this.beforeLoad, this);
31750         ds.un("load", this.onLoad, this);
31751         ds.un("loadexception", this.onLoadError, this);
31752         ds.un("remove", this.updateInfo, this);
31753         ds.un("add", this.updateInfo, this);
31754         this.ds = undefined;
31755     },
31756
31757     /**
31758      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31759      * @param {Roo.data.Store} store The data store to bind
31760      */
31761     bind : function(ds){
31762         ds.on("beforeload", this.beforeLoad, this);
31763         ds.on("load", this.onLoad, this);
31764         ds.on("loadexception", this.onLoadError, this);
31765         ds.on("remove", this.updateInfo, this);
31766         ds.on("add", this.updateInfo, this);
31767         this.ds = ds;
31768     }
31769 });/*
31770  * - LGPL
31771  *
31772  * element
31773  * 
31774  */
31775
31776 /**
31777  * @class Roo.bootstrap.MessageBar
31778  * @extends Roo.bootstrap.Component
31779  * Bootstrap MessageBar class
31780  * @cfg {String} html contents of the MessageBar
31781  * @cfg {String} weight (info | success | warning | danger) default info
31782  * @cfg {String} beforeClass insert the bar before the given class
31783  * @cfg {Boolean} closable (true | false) default false
31784  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31785  * 
31786  * @constructor
31787  * Create a new Element
31788  * @param {Object} config The config object
31789  */
31790
31791 Roo.bootstrap.MessageBar = function(config){
31792     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31793 };
31794
31795 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31796     
31797     html: '',
31798     weight: 'info',
31799     closable: false,
31800     fixed: false,
31801     beforeClass: 'bootstrap-sticky-wrap',
31802     
31803     getAutoCreate : function(){
31804         
31805         var cfg = {
31806             tag: 'div',
31807             cls: 'alert alert-dismissable alert-' + this.weight,
31808             cn: [
31809                 {
31810                     tag: 'span',
31811                     cls: 'message',
31812                     html: this.html || ''
31813                 }
31814             ]
31815         };
31816         
31817         if(this.fixed){
31818             cfg.cls += ' alert-messages-fixed';
31819         }
31820         
31821         if(this.closable){
31822             cfg.cn.push({
31823                 tag: 'button',
31824                 cls: 'close',
31825                 html: 'x'
31826             });
31827         }
31828         
31829         return cfg;
31830     },
31831     
31832     onRender : function(ct, position)
31833     {
31834         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31835         
31836         if(!this.el){
31837             var cfg = Roo.apply({},  this.getAutoCreate());
31838             cfg.id = Roo.id();
31839             
31840             if (this.cls) {
31841                 cfg.cls += ' ' + this.cls;
31842             }
31843             if (this.style) {
31844                 cfg.style = this.style;
31845             }
31846             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31847             
31848             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31849         }
31850         
31851         this.el.select('>button.close').on('click', this.hide, this);
31852         
31853     },
31854     
31855     show : function()
31856     {
31857         if (!this.rendered) {
31858             this.render();
31859         }
31860         
31861         this.el.show();
31862         
31863         this.fireEvent('show', this);
31864         
31865     },
31866     
31867     hide : function()
31868     {
31869         if (!this.rendered) {
31870             this.render();
31871         }
31872         
31873         this.el.hide();
31874         
31875         this.fireEvent('hide', this);
31876     },
31877     
31878     update : function()
31879     {
31880 //        var e = this.el.dom.firstChild;
31881 //        
31882 //        if(this.closable){
31883 //            e = e.nextSibling;
31884 //        }
31885 //        
31886 //        e.data = this.html || '';
31887
31888         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31889     }
31890    
31891 });
31892
31893  
31894
31895      /*
31896  * - LGPL
31897  *
31898  * Graph
31899  * 
31900  */
31901
31902
31903 /**
31904  * @class Roo.bootstrap.Graph
31905  * @extends Roo.bootstrap.Component
31906  * Bootstrap Graph class
31907 > Prameters
31908  -sm {number} sm 4
31909  -md {number} md 5
31910  @cfg {String} graphtype  bar | vbar | pie
31911  @cfg {number} g_x coodinator | centre x (pie)
31912  @cfg {number} g_y coodinator | centre y (pie)
31913  @cfg {number} g_r radius (pie)
31914  @cfg {number} g_height height of the chart (respected by all elements in the set)
31915  @cfg {number} g_width width of the chart (respected by all elements in the set)
31916  @cfg {Object} title The title of the chart
31917     
31918  -{Array}  values
31919  -opts (object) options for the chart 
31920      o {
31921      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31922      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31923      o vgutter (number)
31924      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.
31925      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31926      o to
31927      o stretch (boolean)
31928      o }
31929  -opts (object) options for the pie
31930      o{
31931      o cut
31932      o startAngle (number)
31933      o endAngle (number)
31934      } 
31935  *
31936  * @constructor
31937  * Create a new Input
31938  * @param {Object} config The config object
31939  */
31940
31941 Roo.bootstrap.Graph = function(config){
31942     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31943     
31944     this.addEvents({
31945         // img events
31946         /**
31947          * @event click
31948          * The img click event for the img.
31949          * @param {Roo.EventObject} e
31950          */
31951         "click" : true
31952     });
31953 };
31954
31955 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31956     
31957     sm: 4,
31958     md: 5,
31959     graphtype: 'bar',
31960     g_height: 250,
31961     g_width: 400,
31962     g_x: 50,
31963     g_y: 50,
31964     g_r: 30,
31965     opts:{
31966         //g_colors: this.colors,
31967         g_type: 'soft',
31968         g_gutter: '20%'
31969
31970     },
31971     title : false,
31972
31973     getAutoCreate : function(){
31974         
31975         var cfg = {
31976             tag: 'div',
31977             html : null
31978         };
31979         
31980         
31981         return  cfg;
31982     },
31983
31984     onRender : function(ct,position){
31985         
31986         
31987         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31988         
31989         if (typeof(Raphael) == 'undefined') {
31990             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31991             return;
31992         }
31993         
31994         this.raphael = Raphael(this.el.dom);
31995         
31996                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31997                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31998                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31999                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
32000                 /*
32001                 r.text(160, 10, "Single Series Chart").attr(txtattr);
32002                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
32003                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
32004                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
32005                 
32006                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
32007                 r.barchart(330, 10, 300, 220, data1);
32008                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32009                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32010                 */
32011                 
32012                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32013                 // r.barchart(30, 30, 560, 250,  xdata, {
32014                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32015                 //     axis : "0 0 1 1",
32016                 //     axisxlabels :  xdata
32017                 //     //yvalues : cols,
32018                    
32019                 // });
32020 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32021 //        
32022 //        this.load(null,xdata,{
32023 //                axis : "0 0 1 1",
32024 //                axisxlabels :  xdata
32025 //                });
32026
32027     },
32028
32029     load : function(graphtype,xdata,opts)
32030     {
32031         this.raphael.clear();
32032         if(!graphtype) {
32033             graphtype = this.graphtype;
32034         }
32035         if(!opts){
32036             opts = this.opts;
32037         }
32038         var r = this.raphael,
32039             fin = function () {
32040                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32041             },
32042             fout = function () {
32043                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32044             },
32045             pfin = function() {
32046                 this.sector.stop();
32047                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32048
32049                 if (this.label) {
32050                     this.label[0].stop();
32051                     this.label[0].attr({ r: 7.5 });
32052                     this.label[1].attr({ "font-weight": 800 });
32053                 }
32054             },
32055             pfout = function() {
32056                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32057
32058                 if (this.label) {
32059                     this.label[0].animate({ r: 5 }, 500, "bounce");
32060                     this.label[1].attr({ "font-weight": 400 });
32061                 }
32062             };
32063
32064         switch(graphtype){
32065             case 'bar':
32066                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32067                 break;
32068             case 'hbar':
32069                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32070                 break;
32071             case 'pie':
32072 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
32073 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32074 //            
32075                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32076                 
32077                 break;
32078
32079         }
32080         
32081         if(this.title){
32082             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32083         }
32084         
32085     },
32086     
32087     setTitle: function(o)
32088     {
32089         this.title = o;
32090     },
32091     
32092     initEvents: function() {
32093         
32094         if(!this.href){
32095             this.el.on('click', this.onClick, this);
32096         }
32097     },
32098     
32099     onClick : function(e)
32100     {
32101         Roo.log('img onclick');
32102         this.fireEvent('click', this, e);
32103     }
32104    
32105 });
32106
32107  
32108 Roo.bootstrap.dash = {};/*
32109  * - LGPL
32110  *
32111  * numberBox
32112  * 
32113  */
32114 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32115
32116 /**
32117  * @class Roo.bootstrap.dash.NumberBox
32118  * @extends Roo.bootstrap.Component
32119  * Bootstrap NumberBox class
32120  * @cfg {String} headline Box headline
32121  * @cfg {String} content Box content
32122  * @cfg {String} icon Box icon
32123  * @cfg {String} footer Footer text
32124  * @cfg {String} fhref Footer href
32125  * 
32126  * @constructor
32127  * Create a new NumberBox
32128  * @param {Object} config The config object
32129  */
32130
32131
32132 Roo.bootstrap.dash.NumberBox = function(config){
32133     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32134     
32135 };
32136
32137 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
32138     
32139     headline : '',
32140     content : '',
32141     icon : '',
32142     footer : '',
32143     fhref : '',
32144     ficon : '',
32145     
32146     getAutoCreate : function(){
32147         
32148         var cfg = {
32149             tag : 'div',
32150             cls : 'small-box ',
32151             cn : [
32152                 {
32153                     tag : 'div',
32154                     cls : 'inner',
32155                     cn :[
32156                         {
32157                             tag : 'h3',
32158                             cls : 'roo-headline',
32159                             html : this.headline
32160                         },
32161                         {
32162                             tag : 'p',
32163                             cls : 'roo-content',
32164                             html : this.content
32165                         }
32166                     ]
32167                 }
32168             ]
32169         };
32170         
32171         if(this.icon){
32172             cfg.cn.push({
32173                 tag : 'div',
32174                 cls : 'icon',
32175                 cn :[
32176                     {
32177                         tag : 'i',
32178                         cls : 'ion ' + this.icon
32179                     }
32180                 ]
32181             });
32182         }
32183         
32184         if(this.footer){
32185             var footer = {
32186                 tag : 'a',
32187                 cls : 'small-box-footer',
32188                 href : this.fhref || '#',
32189                 html : this.footer
32190             };
32191             
32192             cfg.cn.push(footer);
32193             
32194         }
32195         
32196         return  cfg;
32197     },
32198
32199     onRender : function(ct,position){
32200         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32201
32202
32203        
32204                 
32205     },
32206
32207     setHeadline: function (value)
32208     {
32209         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32210     },
32211     
32212     setFooter: function (value, href)
32213     {
32214         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32215         
32216         if(href){
32217             this.el.select('a.small-box-footer',true).first().attr('href', href);
32218         }
32219         
32220     },
32221
32222     setContent: function (value)
32223     {
32224         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32225     },
32226
32227     initEvents: function() 
32228     {   
32229         
32230     }
32231     
32232 });
32233
32234  
32235 /*
32236  * - LGPL
32237  *
32238  * TabBox
32239  * 
32240  */
32241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32242
32243 /**
32244  * @class Roo.bootstrap.dash.TabBox
32245  * @extends Roo.bootstrap.Component
32246  * @children Roo.bootstrap.dash.TabPane
32247  * Bootstrap TabBox class
32248  * @cfg {String} title Title of the TabBox
32249  * @cfg {String} icon Icon of the TabBox
32250  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32251  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32252  * 
32253  * @constructor
32254  * Create a new TabBox
32255  * @param {Object} config The config object
32256  */
32257
32258
32259 Roo.bootstrap.dash.TabBox = function(config){
32260     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32261     this.addEvents({
32262         // raw events
32263         /**
32264          * @event addpane
32265          * When a pane is added
32266          * @param {Roo.bootstrap.dash.TabPane} pane
32267          */
32268         "addpane" : true,
32269         /**
32270          * @event activatepane
32271          * When a pane is activated
32272          * @param {Roo.bootstrap.dash.TabPane} pane
32273          */
32274         "activatepane" : true
32275         
32276          
32277     });
32278     
32279     this.panes = [];
32280 };
32281
32282 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32283
32284     title : '',
32285     icon : false,
32286     showtabs : true,
32287     tabScrollable : false,
32288     
32289     getChildContainer : function()
32290     {
32291         return this.el.select('.tab-content', true).first();
32292     },
32293     
32294     getAutoCreate : function(){
32295         
32296         var header = {
32297             tag: 'li',
32298             cls: 'pull-left header',
32299             html: this.title,
32300             cn : []
32301         };
32302         
32303         if(this.icon){
32304             header.cn.push({
32305                 tag: 'i',
32306                 cls: 'fa ' + this.icon
32307             });
32308         }
32309         
32310         var h = {
32311             tag: 'ul',
32312             cls: 'nav nav-tabs pull-right',
32313             cn: [
32314                 header
32315             ]
32316         };
32317         
32318         if(this.tabScrollable){
32319             h = {
32320                 tag: 'div',
32321                 cls: 'tab-header',
32322                 cn: [
32323                     {
32324                         tag: 'ul',
32325                         cls: 'nav nav-tabs pull-right',
32326                         cn: [
32327                             header
32328                         ]
32329                     }
32330                 ]
32331             };
32332         }
32333         
32334         var cfg = {
32335             tag: 'div',
32336             cls: 'nav-tabs-custom',
32337             cn: [
32338                 h,
32339                 {
32340                     tag: 'div',
32341                     cls: 'tab-content no-padding',
32342                     cn: []
32343                 }
32344             ]
32345         };
32346
32347         return  cfg;
32348     },
32349     initEvents : function()
32350     {
32351         //Roo.log('add add pane handler');
32352         this.on('addpane', this.onAddPane, this);
32353     },
32354      /**
32355      * Updates the box title
32356      * @param {String} html to set the title to.
32357      */
32358     setTitle : function(value)
32359     {
32360         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32361     },
32362     onAddPane : function(pane)
32363     {
32364         this.panes.push(pane);
32365         //Roo.log('addpane');
32366         //Roo.log(pane);
32367         // tabs are rendere left to right..
32368         if(!this.showtabs){
32369             return;
32370         }
32371         
32372         var ctr = this.el.select('.nav-tabs', true).first();
32373          
32374          
32375         var existing = ctr.select('.nav-tab',true);
32376         var qty = existing.getCount();;
32377         
32378         
32379         var tab = ctr.createChild({
32380             tag : 'li',
32381             cls : 'nav-tab' + (qty ? '' : ' active'),
32382             cn : [
32383                 {
32384                     tag : 'a',
32385                     href:'#',
32386                     html : pane.title
32387                 }
32388             ]
32389         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32390         pane.tab = tab;
32391         
32392         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32393         if (!qty) {
32394             pane.el.addClass('active');
32395         }
32396         
32397                 
32398     },
32399     onTabClick : function(ev,un,ob,pane)
32400     {
32401         //Roo.log('tab - prev default');
32402         ev.preventDefault();
32403         
32404         
32405         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32406         pane.tab.addClass('active');
32407         //Roo.log(pane.title);
32408         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32409         // technically we should have a deactivate event.. but maybe add later.
32410         // and it should not de-activate the selected tab...
32411         this.fireEvent('activatepane', pane);
32412         pane.el.addClass('active');
32413         pane.fireEvent('activate');
32414         
32415         
32416     },
32417     
32418     getActivePane : function()
32419     {
32420         var r = false;
32421         Roo.each(this.panes, function(p) {
32422             if(p.el.hasClass('active')){
32423                 r = p;
32424                 return false;
32425             }
32426             
32427             return;
32428         });
32429         
32430         return r;
32431     }
32432     
32433     
32434 });
32435
32436  
32437 /*
32438  * - LGPL
32439  *
32440  * Tab pane
32441  * 
32442  */
32443 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32444 /**
32445  * @class Roo.bootstrap.TabPane
32446  * @extends Roo.bootstrap.Component
32447  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32448  * Bootstrap TabPane class
32449  * @cfg {Boolean} active (false | true) Default false
32450  * @cfg {String} title title of panel
32451
32452  * 
32453  * @constructor
32454  * Create a new TabPane
32455  * @param {Object} config The config object
32456  */
32457
32458 Roo.bootstrap.dash.TabPane = function(config){
32459     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32460     
32461     this.addEvents({
32462         // raw events
32463         /**
32464          * @event activate
32465          * When a pane is activated
32466          * @param {Roo.bootstrap.dash.TabPane} pane
32467          */
32468         "activate" : true
32469          
32470     });
32471 };
32472
32473 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32474     
32475     active : false,
32476     title : '',
32477     
32478     // the tabBox that this is attached to.
32479     tab : false,
32480      
32481     getAutoCreate : function() 
32482     {
32483         var cfg = {
32484             tag: 'div',
32485             cls: 'tab-pane'
32486         };
32487         
32488         if(this.active){
32489             cfg.cls += ' active';
32490         }
32491         
32492         return cfg;
32493     },
32494     initEvents  : function()
32495     {
32496         //Roo.log('trigger add pane handler');
32497         this.parent().fireEvent('addpane', this)
32498     },
32499     
32500      /**
32501      * Updates the tab title 
32502      * @param {String} html to set the title to.
32503      */
32504     setTitle: function(str)
32505     {
32506         if (!this.tab) {
32507             return;
32508         }
32509         this.title = str;
32510         this.tab.select('a', true).first().dom.innerHTML = str;
32511         
32512     }
32513     
32514     
32515     
32516 });
32517
32518  
32519
32520
32521  /*
32522  * - LGPL
32523  *
32524  * Tooltip
32525  * 
32526  */
32527
32528 /**
32529  * @class Roo.bootstrap.Tooltip
32530  * Bootstrap Tooltip class
32531  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32532  * to determine which dom element triggers the tooltip.
32533  * 
32534  * It needs to add support for additional attributes like tooltip-position
32535  * 
32536  * @constructor
32537  * Create a new Toolti
32538  * @param {Object} config The config object
32539  */
32540
32541 Roo.bootstrap.Tooltip = function(config){
32542     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32543     
32544     this.alignment = Roo.bootstrap.Tooltip.alignment;
32545     
32546     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32547         this.alignment = config.alignment;
32548     }
32549     
32550 };
32551
32552 Roo.apply(Roo.bootstrap.Tooltip, {
32553     /**
32554      * @function init initialize tooltip monitoring.
32555      * @static
32556      */
32557     currentEl : false,
32558     currentTip : false,
32559     currentRegion : false,
32560     
32561     //  init : delay?
32562     
32563     init : function()
32564     {
32565         Roo.get(document).on('mouseover', this.enter ,this);
32566         Roo.get(document).on('mouseout', this.leave, this);
32567          
32568         
32569         this.currentTip = new Roo.bootstrap.Tooltip();
32570     },
32571     
32572     enter : function(ev)
32573     {
32574         var dom = ev.getTarget();
32575         
32576         //Roo.log(['enter',dom]);
32577         var el = Roo.fly(dom);
32578         if (this.currentEl) {
32579             //Roo.log(dom);
32580             //Roo.log(this.currentEl);
32581             //Roo.log(this.currentEl.contains(dom));
32582             if (this.currentEl == el) {
32583                 return;
32584             }
32585             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32586                 return;
32587             }
32588
32589         }
32590         
32591         if (this.currentTip.el) {
32592             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32593         }    
32594         //Roo.log(ev);
32595         
32596         if(!el || el.dom == document){
32597             return;
32598         }
32599         
32600         var bindEl = el; 
32601         var pel = false;
32602         if (!el.attr('tooltip')) {
32603             pel = el.findParent("[tooltip]");
32604             if (pel) {
32605                 bindEl = Roo.get(pel);
32606             }
32607         }
32608         
32609        
32610         
32611         // you can not look for children, as if el is the body.. then everythign is the child..
32612         if (!pel && !el.attr('tooltip')) { //
32613             if (!el.select("[tooltip]").elements.length) {
32614                 return;
32615             }
32616             // is the mouse over this child...?
32617             bindEl = el.select("[tooltip]").first();
32618             var xy = ev.getXY();
32619             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32620                 //Roo.log("not in region.");
32621                 return;
32622             }
32623             //Roo.log("child element over..");
32624             
32625         }
32626         this.currentEl = el;
32627         this.currentTip.bind(bindEl);
32628         this.currentRegion = Roo.lib.Region.getRegion(dom);
32629         this.currentTip.enter();
32630         
32631     },
32632     leave : function(ev)
32633     {
32634         var dom = ev.getTarget();
32635         //Roo.log(['leave',dom]);
32636         if (!this.currentEl) {
32637             return;
32638         }
32639         
32640         
32641         if (dom != this.currentEl.dom) {
32642             return;
32643         }
32644         var xy = ev.getXY();
32645         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32646             return;
32647         }
32648         // only activate leave if mouse cursor is outside... bounding box..
32649         
32650         
32651         
32652         
32653         if (this.currentTip) {
32654             this.currentTip.leave();
32655         }
32656         //Roo.log('clear currentEl');
32657         this.currentEl = false;
32658         
32659         
32660     },
32661     alignment : {
32662         'left' : ['r-l', [-2,0], 'right'],
32663         'right' : ['l-r', [2,0], 'left'],
32664         'bottom' : ['t-b', [0,2], 'top'],
32665         'top' : [ 'b-t', [0,-2], 'bottom']
32666     }
32667     
32668 });
32669
32670
32671 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32672     
32673     
32674     bindEl : false,
32675     
32676     delay : null, // can be { show : 300 , hide: 500}
32677     
32678     timeout : null,
32679     
32680     hoverState : null, //???
32681     
32682     placement : 'bottom', 
32683     
32684     alignment : false,
32685     
32686     getAutoCreate : function(){
32687     
32688         var cfg = {
32689            cls : 'tooltip',   
32690            role : 'tooltip',
32691            cn : [
32692                 {
32693                     cls : 'tooltip-arrow arrow'
32694                 },
32695                 {
32696                     cls : 'tooltip-inner'
32697                 }
32698            ]
32699         };
32700         
32701         return cfg;
32702     },
32703     bind : function(el)
32704     {
32705         this.bindEl = el;
32706     },
32707     
32708     initEvents : function()
32709     {
32710         this.arrowEl = this.el.select('.arrow', true).first();
32711         this.innerEl = this.el.select('.tooltip-inner', true).first();
32712     },
32713     
32714     enter : function () {
32715        
32716         if (this.timeout != null) {
32717             clearTimeout(this.timeout);
32718         }
32719         
32720         this.hoverState = 'in';
32721          //Roo.log("enter - show");
32722         if (!this.delay || !this.delay.show) {
32723             this.show();
32724             return;
32725         }
32726         var _t = this;
32727         this.timeout = setTimeout(function () {
32728             if (_t.hoverState == 'in') {
32729                 _t.show();
32730             }
32731         }, this.delay.show);
32732     },
32733     leave : function()
32734     {
32735         clearTimeout(this.timeout);
32736     
32737         this.hoverState = 'out';
32738          if (!this.delay || !this.delay.hide) {
32739             this.hide();
32740             return;
32741         }
32742        
32743         var _t = this;
32744         this.timeout = setTimeout(function () {
32745             //Roo.log("leave - timeout");
32746             
32747             if (_t.hoverState == 'out') {
32748                 _t.hide();
32749                 Roo.bootstrap.Tooltip.currentEl = false;
32750             }
32751         }, delay);
32752     },
32753     
32754     show : function (msg)
32755     {
32756         if (!this.el) {
32757             this.render(document.body);
32758         }
32759         // set content.
32760         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32761         
32762         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32763         
32764         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32765         
32766         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32767                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32768
32769         this.el.addClass(this.bindEl.attr('tooltip-class'));
32770         
32771         var placement = typeof this.placement == 'function' ?
32772             this.placement.call(this, this.el, on_el) :
32773             this.placement;
32774             
32775         var autoToken = /\s?auto?\s?/i;
32776         var autoPlace = autoToken.test(placement);
32777         if (autoPlace) {
32778             placement = placement.replace(autoToken, '') || 'top';
32779         }
32780         
32781         //this.el.detach()
32782         //this.el.setXY([0,0]);
32783         this.el.show();
32784         //this.el.dom.style.display='block';
32785         
32786         //this.el.appendTo(on_el);
32787         
32788         var p = this.getPosition();
32789         var box = this.el.getBox();
32790         
32791         if (autoPlace) {
32792             // fixme..
32793         }
32794         
32795         var align = this.alignment[placement];
32796         
32797         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32798         
32799         if(placement == 'top' || placement == 'bottom'){
32800             if(xy[0] < 0){
32801                 placement = 'right';
32802             }
32803             
32804             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32805                 placement = 'left';
32806             }
32807             
32808             var scroll = Roo.select('body', true).first().getScroll();
32809             
32810             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32811                 placement = 'top';
32812             }
32813             
32814             align = this.alignment[placement];
32815             
32816             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32817             
32818         }
32819         
32820         var elems = document.getElementsByTagName('div');
32821         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32822         for (var i = 0; i < elems.length; i++) {
32823           var zindex = Number.parseInt(
32824                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32825                 10
32826           );
32827           if (zindex > highest) {
32828             highest = zindex;
32829           }
32830         }
32831         
32832         
32833         
32834         this.el.dom.style.zIndex = highest;
32835         
32836         this.el.alignTo(this.bindEl, align[0],align[1]);
32837         //var arrow = this.el.select('.arrow',true).first();
32838         //arrow.set(align[2], 
32839         
32840         this.el.addClass(placement);
32841         this.el.addClass("bs-tooltip-"+ placement);
32842         
32843         this.el.addClass('in fade show');
32844         
32845         this.hoverState = null;
32846         
32847         if (this.el.hasClass('fade')) {
32848             // fade it?
32849         }
32850         
32851         
32852         
32853         
32854         
32855     },
32856     hide : function()
32857     {
32858          
32859         if (!this.el) {
32860             return;
32861         }
32862         //this.el.setXY([0,0]);
32863         this.el.addClass(this.bindEl.attr('tooltip-class'));
32864         this.el.removeClass(['show', 'in']);
32865         //this.el.hide();
32866         
32867     }
32868     
32869 });
32870  
32871
32872  /*
32873  * - LGPL
32874  *
32875  * Location Picker
32876  * 
32877  */
32878
32879 /**
32880  * @class Roo.bootstrap.LocationPicker
32881  * @extends Roo.bootstrap.Component
32882  * Bootstrap LocationPicker class
32883  * @cfg {Number} latitude Position when init default 0
32884  * @cfg {Number} longitude Position when init default 0
32885  * @cfg {Number} zoom default 15
32886  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32887  * @cfg {Boolean} mapTypeControl default false
32888  * @cfg {Boolean} disableDoubleClickZoom default false
32889  * @cfg {Boolean} scrollwheel default true
32890  * @cfg {Boolean} streetViewControl default false
32891  * @cfg {Number} radius default 0
32892  * @cfg {String} locationName
32893  * @cfg {Boolean} draggable default true
32894  * @cfg {Boolean} enableAutocomplete default false
32895  * @cfg {Boolean} enableReverseGeocode default true
32896  * @cfg {String} markerTitle
32897  * 
32898  * @constructor
32899  * Create a new LocationPicker
32900  * @param {Object} config The config object
32901  */
32902
32903
32904 Roo.bootstrap.LocationPicker = function(config){
32905     
32906     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32907     
32908     this.addEvents({
32909         /**
32910          * @event initial
32911          * Fires when the picker initialized.
32912          * @param {Roo.bootstrap.LocationPicker} this
32913          * @param {Google Location} location
32914          */
32915         initial : true,
32916         /**
32917          * @event positionchanged
32918          * Fires when the picker position changed.
32919          * @param {Roo.bootstrap.LocationPicker} this
32920          * @param {Google Location} location
32921          */
32922         positionchanged : true,
32923         /**
32924          * @event resize
32925          * Fires when the map resize.
32926          * @param {Roo.bootstrap.LocationPicker} this
32927          */
32928         resize : true,
32929         /**
32930          * @event show
32931          * Fires when the map show.
32932          * @param {Roo.bootstrap.LocationPicker} this
32933          */
32934         show : true,
32935         /**
32936          * @event hide
32937          * Fires when the map hide.
32938          * @param {Roo.bootstrap.LocationPicker} this
32939          */
32940         hide : true,
32941         /**
32942          * @event mapClick
32943          * Fires when click the map.
32944          * @param {Roo.bootstrap.LocationPicker} this
32945          * @param {Map event} e
32946          */
32947         mapClick : true,
32948         /**
32949          * @event mapRightClick
32950          * Fires when right click the map.
32951          * @param {Roo.bootstrap.LocationPicker} this
32952          * @param {Map event} e
32953          */
32954         mapRightClick : true,
32955         /**
32956          * @event markerClick
32957          * Fires when click the marker.
32958          * @param {Roo.bootstrap.LocationPicker} this
32959          * @param {Map event} e
32960          */
32961         markerClick : true,
32962         /**
32963          * @event markerRightClick
32964          * Fires when right click the marker.
32965          * @param {Roo.bootstrap.LocationPicker} this
32966          * @param {Map event} e
32967          */
32968         markerRightClick : true,
32969         /**
32970          * @event OverlayViewDraw
32971          * Fires when OverlayView Draw
32972          * @param {Roo.bootstrap.LocationPicker} this
32973          */
32974         OverlayViewDraw : true,
32975         /**
32976          * @event OverlayViewOnAdd
32977          * Fires when OverlayView Draw
32978          * @param {Roo.bootstrap.LocationPicker} this
32979          */
32980         OverlayViewOnAdd : true,
32981         /**
32982          * @event OverlayViewOnRemove
32983          * Fires when OverlayView Draw
32984          * @param {Roo.bootstrap.LocationPicker} this
32985          */
32986         OverlayViewOnRemove : true,
32987         /**
32988          * @event OverlayViewShow
32989          * Fires when OverlayView Draw
32990          * @param {Roo.bootstrap.LocationPicker} this
32991          * @param {Pixel} cpx
32992          */
32993         OverlayViewShow : true,
32994         /**
32995          * @event OverlayViewHide
32996          * Fires when OverlayView Draw
32997          * @param {Roo.bootstrap.LocationPicker} this
32998          */
32999         OverlayViewHide : true,
33000         /**
33001          * @event loadexception
33002          * Fires when load google lib failed.
33003          * @param {Roo.bootstrap.LocationPicker} this
33004          */
33005         loadexception : true
33006     });
33007         
33008 };
33009
33010 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33011     
33012     gMapContext: false,
33013     
33014     latitude: 0,
33015     longitude: 0,
33016     zoom: 15,
33017     mapTypeId: false,
33018     mapTypeControl: false,
33019     disableDoubleClickZoom: false,
33020     scrollwheel: true,
33021     streetViewControl: false,
33022     radius: 0,
33023     locationName: '',
33024     draggable: true,
33025     enableAutocomplete: false,
33026     enableReverseGeocode: true,
33027     markerTitle: '',
33028     
33029     getAutoCreate: function()
33030     {
33031
33032         var cfg = {
33033             tag: 'div',
33034             cls: 'roo-location-picker'
33035         };
33036         
33037         return cfg
33038     },
33039     
33040     initEvents: function(ct, position)
33041     {       
33042         if(!this.el.getWidth() || this.isApplied()){
33043             return;
33044         }
33045         
33046         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33047         
33048         this.initial();
33049     },
33050     
33051     initial: function()
33052     {
33053         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33054             this.fireEvent('loadexception', this);
33055             return;
33056         }
33057         
33058         if(!this.mapTypeId){
33059             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33060         }
33061         
33062         this.gMapContext = this.GMapContext();
33063         
33064         this.initOverlayView();
33065         
33066         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33067         
33068         var _this = this;
33069                 
33070         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33071             _this.setPosition(_this.gMapContext.marker.position);
33072         });
33073         
33074         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33075             _this.fireEvent('mapClick', this, event);
33076             
33077         });
33078
33079         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33080             _this.fireEvent('mapRightClick', this, event);
33081             
33082         });
33083         
33084         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33085             _this.fireEvent('markerClick', this, event);
33086             
33087         });
33088
33089         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33090             _this.fireEvent('markerRightClick', this, event);
33091             
33092         });
33093         
33094         this.setPosition(this.gMapContext.location);
33095         
33096         this.fireEvent('initial', this, this.gMapContext.location);
33097     },
33098     
33099     initOverlayView: function()
33100     {
33101         var _this = this;
33102         
33103         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33104             
33105             draw: function()
33106             {
33107                 _this.fireEvent('OverlayViewDraw', _this);
33108             },
33109             
33110             onAdd: function()
33111             {
33112                 _this.fireEvent('OverlayViewOnAdd', _this);
33113             },
33114             
33115             onRemove: function()
33116             {
33117                 _this.fireEvent('OverlayViewOnRemove', _this);
33118             },
33119             
33120             show: function(cpx)
33121             {
33122                 _this.fireEvent('OverlayViewShow', _this, cpx);
33123             },
33124             
33125             hide: function()
33126             {
33127                 _this.fireEvent('OverlayViewHide', _this);
33128             }
33129             
33130         });
33131     },
33132     
33133     fromLatLngToContainerPixel: function(event)
33134     {
33135         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33136     },
33137     
33138     isApplied: function() 
33139     {
33140         return this.getGmapContext() == false ? false : true;
33141     },
33142     
33143     getGmapContext: function() 
33144     {
33145         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33146     },
33147     
33148     GMapContext: function() 
33149     {
33150         var position = new google.maps.LatLng(this.latitude, this.longitude);
33151         
33152         var _map = new google.maps.Map(this.el.dom, {
33153             center: position,
33154             zoom: this.zoom,
33155             mapTypeId: this.mapTypeId,
33156             mapTypeControl: this.mapTypeControl,
33157             disableDoubleClickZoom: this.disableDoubleClickZoom,
33158             scrollwheel: this.scrollwheel,
33159             streetViewControl: this.streetViewControl,
33160             locationName: this.locationName,
33161             draggable: this.draggable,
33162             enableAutocomplete: this.enableAutocomplete,
33163             enableReverseGeocode: this.enableReverseGeocode
33164         });
33165         
33166         var _marker = new google.maps.Marker({
33167             position: position,
33168             map: _map,
33169             title: this.markerTitle,
33170             draggable: this.draggable
33171         });
33172         
33173         return {
33174             map: _map,
33175             marker: _marker,
33176             circle: null,
33177             location: position,
33178             radius: this.radius,
33179             locationName: this.locationName,
33180             addressComponents: {
33181                 formatted_address: null,
33182                 addressLine1: null,
33183                 addressLine2: null,
33184                 streetName: null,
33185                 streetNumber: null,
33186                 city: null,
33187                 district: null,
33188                 state: null,
33189                 stateOrProvince: null
33190             },
33191             settings: this,
33192             domContainer: this.el.dom,
33193             geodecoder: new google.maps.Geocoder()
33194         };
33195     },
33196     
33197     drawCircle: function(center, radius, options) 
33198     {
33199         if (this.gMapContext.circle != null) {
33200             this.gMapContext.circle.setMap(null);
33201         }
33202         if (radius > 0) {
33203             radius *= 1;
33204             options = Roo.apply({}, options, {
33205                 strokeColor: "#0000FF",
33206                 strokeOpacity: .35,
33207                 strokeWeight: 2,
33208                 fillColor: "#0000FF",
33209                 fillOpacity: .2
33210             });
33211             
33212             options.map = this.gMapContext.map;
33213             options.radius = radius;
33214             options.center = center;
33215             this.gMapContext.circle = new google.maps.Circle(options);
33216             return this.gMapContext.circle;
33217         }
33218         
33219         return null;
33220     },
33221     
33222     setPosition: function(location) 
33223     {
33224         this.gMapContext.location = location;
33225         this.gMapContext.marker.setPosition(location);
33226         this.gMapContext.map.panTo(location);
33227         this.drawCircle(location, this.gMapContext.radius, {});
33228         
33229         var _this = this;
33230         
33231         if (this.gMapContext.settings.enableReverseGeocode) {
33232             this.gMapContext.geodecoder.geocode({
33233                 latLng: this.gMapContext.location
33234             }, function(results, status) {
33235                 
33236                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33237                     _this.gMapContext.locationName = results[0].formatted_address;
33238                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33239                     
33240                     _this.fireEvent('positionchanged', this, location);
33241                 }
33242             });
33243             
33244             return;
33245         }
33246         
33247         this.fireEvent('positionchanged', this, location);
33248     },
33249     
33250     resize: function()
33251     {
33252         google.maps.event.trigger(this.gMapContext.map, "resize");
33253         
33254         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33255         
33256         this.fireEvent('resize', this);
33257     },
33258     
33259     setPositionByLatLng: function(latitude, longitude)
33260     {
33261         this.setPosition(new google.maps.LatLng(latitude, longitude));
33262     },
33263     
33264     getCurrentPosition: function() 
33265     {
33266         return {
33267             latitude: this.gMapContext.location.lat(),
33268             longitude: this.gMapContext.location.lng()
33269         };
33270     },
33271     
33272     getAddressName: function() 
33273     {
33274         return this.gMapContext.locationName;
33275     },
33276     
33277     getAddressComponents: function() 
33278     {
33279         return this.gMapContext.addressComponents;
33280     },
33281     
33282     address_component_from_google_geocode: function(address_components) 
33283     {
33284         var result = {};
33285         
33286         for (var i = 0; i < address_components.length; i++) {
33287             var component = address_components[i];
33288             if (component.types.indexOf("postal_code") >= 0) {
33289                 result.postalCode = component.short_name;
33290             } else if (component.types.indexOf("street_number") >= 0) {
33291                 result.streetNumber = component.short_name;
33292             } else if (component.types.indexOf("route") >= 0) {
33293                 result.streetName = component.short_name;
33294             } else if (component.types.indexOf("neighborhood") >= 0) {
33295                 result.city = component.short_name;
33296             } else if (component.types.indexOf("locality") >= 0) {
33297                 result.city = component.short_name;
33298             } else if (component.types.indexOf("sublocality") >= 0) {
33299                 result.district = component.short_name;
33300             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33301                 result.stateOrProvince = component.short_name;
33302             } else if (component.types.indexOf("country") >= 0) {
33303                 result.country = component.short_name;
33304             }
33305         }
33306         
33307         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33308         result.addressLine2 = "";
33309         return result;
33310     },
33311     
33312     setZoomLevel: function(zoom)
33313     {
33314         this.gMapContext.map.setZoom(zoom);
33315     },
33316     
33317     show: function()
33318     {
33319         if(!this.el){
33320             return;
33321         }
33322         
33323         this.el.show();
33324         
33325         this.resize();
33326         
33327         this.fireEvent('show', this);
33328     },
33329     
33330     hide: function()
33331     {
33332         if(!this.el){
33333             return;
33334         }
33335         
33336         this.el.hide();
33337         
33338         this.fireEvent('hide', this);
33339     }
33340     
33341 });
33342
33343 Roo.apply(Roo.bootstrap.LocationPicker, {
33344     
33345     OverlayView : function(map, options)
33346     {
33347         options = options || {};
33348         
33349         this.setMap(map);
33350     }
33351     
33352     
33353 });/**
33354  * @class Roo.bootstrap.Alert
33355  * @extends Roo.bootstrap.Component
33356  * Bootstrap Alert class - shows an alert area box
33357  * eg
33358  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33359   Enter a valid email address
33360 </div>
33361  * @licence LGPL
33362  * @cfg {String} title The title of alert
33363  * @cfg {String} html The content of alert
33364  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33365  * @cfg {String} fa font-awesomeicon
33366  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33367  * @cfg {Boolean} close true to show a x closer
33368  * 
33369  * 
33370  * @constructor
33371  * Create a new alert
33372  * @param {Object} config The config object
33373  */
33374
33375
33376 Roo.bootstrap.Alert = function(config){
33377     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33378     
33379 };
33380
33381 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33382     
33383     title: '',
33384     html: '',
33385     weight: false,
33386     fa: false,
33387     faicon: false, // BC
33388     close : false,
33389     
33390     
33391     getAutoCreate : function()
33392     {
33393         
33394         var cfg = {
33395             tag : 'div',
33396             cls : 'alert',
33397             cn : [
33398                 {
33399                     tag: 'button',
33400                     type :  "button",
33401                     cls: "close",
33402                     html : '×',
33403                     style : this.close ? '' : 'display:none'
33404                 },
33405                 {
33406                     tag : 'i',
33407                     cls : 'roo-alert-icon'
33408                     
33409                 },
33410                 {
33411                     tag : 'b',
33412                     cls : 'roo-alert-title',
33413                     html : this.title
33414                 },
33415                 {
33416                     tag : 'span',
33417                     cls : 'roo-alert-text',
33418                     html : this.html
33419                 }
33420             ]
33421         };
33422         
33423         if(this.faicon){
33424             cfg.cn[0].cls += ' fa ' + this.faicon;
33425         }
33426         if(this.fa){
33427             cfg.cn[0].cls += ' fa ' + this.fa;
33428         }
33429         
33430         if(this.weight){
33431             cfg.cls += ' alert-' + this.weight;
33432         }
33433         
33434         return cfg;
33435     },
33436     
33437     initEvents: function() 
33438     {
33439         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33440         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33441         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33442         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33443         if (this.seconds > 0) {
33444             this.hide.defer(this.seconds, this);
33445         }
33446     },
33447     /**
33448      * Set the Title Message HTML
33449      * @param {String} html
33450      */
33451     setTitle : function(str)
33452     {
33453         this.titleEl.dom.innerHTML = str;
33454     },
33455      
33456      /**
33457      * Set the Body Message HTML
33458      * @param {String} html
33459      */
33460     setHtml : function(str)
33461     {
33462         this.htmlEl.dom.innerHTML = str;
33463     },
33464     /**
33465      * Set the Weight of the alert
33466      * @param {String} (success|info|warning|danger) weight
33467      */
33468     
33469     setWeight : function(weight)
33470     {
33471         if(this.weight){
33472             this.el.removeClass('alert-' + this.weight);
33473         }
33474         
33475         this.weight = weight;
33476         
33477         this.el.addClass('alert-' + this.weight);
33478     },
33479       /**
33480      * Set the Icon of the alert
33481      * @param {String} see fontawsome names (name without the 'fa-' bit)
33482      */
33483     setIcon : function(icon)
33484     {
33485         if(this.faicon){
33486             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33487         }
33488         
33489         this.faicon = icon;
33490         
33491         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33492     },
33493     /**
33494      * Hide the Alert
33495      */
33496     hide: function() 
33497     {
33498         this.el.hide();   
33499     },
33500     /**
33501      * Show the Alert
33502      */
33503     show: function() 
33504     {  
33505         this.el.show();   
33506     }
33507     
33508 });
33509
33510  
33511 /*
33512 * Licence: LGPL
33513 */
33514
33515 /**
33516  * @class Roo.bootstrap.UploadCropbox
33517  * @extends Roo.bootstrap.Component
33518  * Bootstrap UploadCropbox class
33519  * @cfg {String} emptyText show when image has been loaded
33520  * @cfg {String} rotateNotify show when image too small to rotate
33521  * @cfg {Number} errorTimeout default 3000
33522  * @cfg {Number} minWidth default 300
33523  * @cfg {Number} minHeight default 300
33524  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33525  * @cfg {Boolean} isDocument (true|false) default false
33526  * @cfg {String} url action url
33527  * @cfg {String} paramName default 'imageUpload'
33528  * @cfg {String} method default POST
33529  * @cfg {Boolean} loadMask (true|false) default true
33530  * @cfg {Boolean} loadingText default 'Loading...'
33531  * 
33532  * @constructor
33533  * Create a new UploadCropbox
33534  * @param {Object} config The config object
33535  */
33536
33537 Roo.bootstrap.UploadCropbox = function(config){
33538     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33539     
33540     this.addEvents({
33541         /**
33542          * @event beforeselectfile
33543          * Fire before select file
33544          * @param {Roo.bootstrap.UploadCropbox} this
33545          */
33546         "beforeselectfile" : true,
33547         /**
33548          * @event initial
33549          * Fire after initEvent
33550          * @param {Roo.bootstrap.UploadCropbox} this
33551          */
33552         "initial" : true,
33553         /**
33554          * @event crop
33555          * Fire after initEvent
33556          * @param {Roo.bootstrap.UploadCropbox} this
33557          * @param {String} data
33558          */
33559         "crop" : true,
33560         /**
33561          * @event prepare
33562          * Fire when preparing the file data
33563          * @param {Roo.bootstrap.UploadCropbox} this
33564          * @param {Object} file
33565          */
33566         "prepare" : true,
33567         /**
33568          * @event exception
33569          * Fire when get exception
33570          * @param {Roo.bootstrap.UploadCropbox} this
33571          * @param {XMLHttpRequest} xhr
33572          */
33573         "exception" : true,
33574         /**
33575          * @event beforeloadcanvas
33576          * Fire before load the canvas
33577          * @param {Roo.bootstrap.UploadCropbox} this
33578          * @param {String} src
33579          */
33580         "beforeloadcanvas" : true,
33581         /**
33582          * @event trash
33583          * Fire when trash image
33584          * @param {Roo.bootstrap.UploadCropbox} this
33585          */
33586         "trash" : true,
33587         /**
33588          * @event download
33589          * Fire when download the image
33590          * @param {Roo.bootstrap.UploadCropbox} this
33591          */
33592         "download" : true,
33593         /**
33594          * @event footerbuttonclick
33595          * Fire when footerbuttonclick
33596          * @param {Roo.bootstrap.UploadCropbox} this
33597          * @param {String} type
33598          */
33599         "footerbuttonclick" : true,
33600         /**
33601          * @event resize
33602          * Fire when resize
33603          * @param {Roo.bootstrap.UploadCropbox} this
33604          */
33605         "resize" : true,
33606         /**
33607          * @event rotate
33608          * Fire when rotate the image
33609          * @param {Roo.bootstrap.UploadCropbox} this
33610          * @param {String} pos
33611          */
33612         "rotate" : true,
33613         /**
33614          * @event inspect
33615          * Fire when inspect the file
33616          * @param {Roo.bootstrap.UploadCropbox} this
33617          * @param {Object} file
33618          */
33619         "inspect" : true,
33620         /**
33621          * @event upload
33622          * Fire when xhr upload the file
33623          * @param {Roo.bootstrap.UploadCropbox} this
33624          * @param {Object} data
33625          */
33626         "upload" : true,
33627         /**
33628          * @event arrange
33629          * Fire when arrange the file data
33630          * @param {Roo.bootstrap.UploadCropbox} this
33631          * @param {Object} formData
33632          */
33633         "arrange" : true
33634     });
33635     
33636     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33637 };
33638
33639 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33640     
33641     emptyText : 'Click to upload image',
33642     rotateNotify : 'Image is too small to rotate',
33643     errorTimeout : 3000,
33644     scale : 0,
33645     baseScale : 1,
33646     rotate : 0,
33647     dragable : false,
33648     pinching : false,
33649     mouseX : 0,
33650     mouseY : 0,
33651     cropData : false,
33652     minWidth : 300,
33653     minHeight : 300,
33654     file : false,
33655     exif : {},
33656     baseRotate : 1,
33657     cropType : 'image/jpeg',
33658     buttons : false,
33659     canvasLoaded : false,
33660     isDocument : false,
33661     method : 'POST',
33662     paramName : 'imageUpload',
33663     loadMask : true,
33664     loadingText : 'Loading...',
33665     maskEl : false,
33666     
33667     getAutoCreate : function()
33668     {
33669         var cfg = {
33670             tag : 'div',
33671             cls : 'roo-upload-cropbox',
33672             cn : [
33673                 {
33674                     tag : 'input',
33675                     cls : 'roo-upload-cropbox-selector',
33676                     type : 'file'
33677                 },
33678                 {
33679                     tag : 'div',
33680                     cls : 'roo-upload-cropbox-body',
33681                     style : 'cursor:pointer',
33682                     cn : [
33683                         {
33684                             tag : 'div',
33685                             cls : 'roo-upload-cropbox-preview'
33686                         },
33687                         {
33688                             tag : 'div',
33689                             cls : 'roo-upload-cropbox-thumb'
33690                         },
33691                         {
33692                             tag : 'div',
33693                             cls : 'roo-upload-cropbox-empty-notify',
33694                             html : this.emptyText
33695                         },
33696                         {
33697                             tag : 'div',
33698                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33699                             html : this.rotateNotify
33700                         }
33701                     ]
33702                 },
33703                 {
33704                     tag : 'div',
33705                     cls : 'roo-upload-cropbox-footer',
33706                     cn : {
33707                         tag : 'div',
33708                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33709                         cn : []
33710                     }
33711                 }
33712             ]
33713         };
33714         
33715         return cfg;
33716     },
33717     
33718     onRender : function(ct, position)
33719     {
33720         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33721         
33722         if (this.buttons.length) {
33723             
33724             Roo.each(this.buttons, function(bb) {
33725                 
33726                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33727                 
33728                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33729                 
33730             }, this);
33731         }
33732         
33733         if(this.loadMask){
33734             this.maskEl = this.el;
33735         }
33736     },
33737     
33738     initEvents : function()
33739     {
33740         this.urlAPI = (window.createObjectURL && window) || 
33741                                 (window.URL && URL.revokeObjectURL && URL) || 
33742                                 (window.webkitURL && webkitURL);
33743                         
33744         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33745         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33746         
33747         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33748         this.selectorEl.hide();
33749         
33750         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33751         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33752         
33753         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33754         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33755         this.thumbEl.hide();
33756         
33757         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33758         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33759         
33760         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33761         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33762         this.errorEl.hide();
33763         
33764         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33765         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33766         this.footerEl.hide();
33767         
33768         this.setThumbBoxSize();
33769         
33770         this.bind();
33771         
33772         this.resize();
33773         
33774         this.fireEvent('initial', this);
33775     },
33776
33777     bind : function()
33778     {
33779         var _this = this;
33780         
33781         window.addEventListener("resize", function() { _this.resize(); } );
33782         
33783         this.bodyEl.on('click', this.beforeSelectFile, this);
33784         
33785         if(Roo.isTouch){
33786             this.bodyEl.on('touchstart', this.onTouchStart, this);
33787             this.bodyEl.on('touchmove', this.onTouchMove, this);
33788             this.bodyEl.on('touchend', this.onTouchEnd, this);
33789         }
33790         
33791         if(!Roo.isTouch){
33792             this.bodyEl.on('mousedown', this.onMouseDown, this);
33793             this.bodyEl.on('mousemove', this.onMouseMove, this);
33794             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33795             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33796             Roo.get(document).on('mouseup', this.onMouseUp, this);
33797         }
33798         
33799         this.selectorEl.on('change', this.onFileSelected, this);
33800     },
33801     
33802     reset : function()
33803     {    
33804         this.scale = 0;
33805         this.baseScale = 1;
33806         this.rotate = 0;
33807         this.baseRotate = 1;
33808         this.dragable = false;
33809         this.pinching = false;
33810         this.mouseX = 0;
33811         this.mouseY = 0;
33812         this.cropData = false;
33813         this.notifyEl.dom.innerHTML = this.emptyText;
33814         
33815         this.selectorEl.dom.value = '';
33816         
33817     },
33818     
33819     resize : function()
33820     {
33821         if(this.fireEvent('resize', this) != false){
33822             this.setThumbBoxPosition();
33823             this.setCanvasPosition();
33824         }
33825     },
33826     
33827     onFooterButtonClick : function(e, el, o, type)
33828     {
33829         switch (type) {
33830             case 'rotate-left' :
33831                 this.onRotateLeft(e);
33832                 break;
33833             case 'rotate-right' :
33834                 this.onRotateRight(e);
33835                 break;
33836             case 'picture' :
33837                 this.beforeSelectFile(e);
33838                 break;
33839             case 'trash' :
33840                 this.trash(e);
33841                 break;
33842             case 'crop' :
33843                 this.crop(e);
33844                 break;
33845             case 'download' :
33846                 this.download(e);
33847                 break;
33848             default :
33849                 break;
33850         }
33851         
33852         this.fireEvent('footerbuttonclick', this, type);
33853     },
33854     
33855     beforeSelectFile : function(e)
33856     {
33857         e.preventDefault();
33858         
33859         if(this.fireEvent('beforeselectfile', this) != false){
33860             this.selectorEl.dom.click();
33861         }
33862     },
33863     
33864     onFileSelected : function(e)
33865     {
33866         e.preventDefault();
33867         
33868         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33869             return;
33870         }
33871         
33872         var file = this.selectorEl.dom.files[0];
33873         
33874         if(this.fireEvent('inspect', this, file) != false){
33875             this.prepare(file);
33876         }
33877         
33878     },
33879     
33880     trash : function(e)
33881     {
33882         this.fireEvent('trash', this);
33883     },
33884     
33885     download : function(e)
33886     {
33887         this.fireEvent('download', this);
33888     },
33889     
33890     loadCanvas : function(src)
33891     {   
33892         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33893             
33894             this.reset();
33895             
33896             this.imageEl = document.createElement('img');
33897             
33898             var _this = this;
33899             
33900             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33901             
33902             this.imageEl.src = src;
33903         }
33904     },
33905     
33906     onLoadCanvas : function()
33907     {   
33908         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33909         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33910         
33911         this.bodyEl.un('click', this.beforeSelectFile, this);
33912         
33913         this.notifyEl.hide();
33914         this.thumbEl.show();
33915         this.footerEl.show();
33916         
33917         this.baseRotateLevel();
33918         
33919         if(this.isDocument){
33920             this.setThumbBoxSize();
33921         }
33922         
33923         this.setThumbBoxPosition();
33924         
33925         this.baseScaleLevel();
33926         
33927         this.draw();
33928         
33929         this.resize();
33930         
33931         this.canvasLoaded = true;
33932         
33933         if(this.loadMask){
33934             this.maskEl.unmask();
33935         }
33936         
33937     },
33938     
33939     setCanvasPosition : function()
33940     {   
33941         if(!this.canvasEl){
33942             return;
33943         }
33944         
33945         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33946         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33947         
33948         this.previewEl.setLeft(pw);
33949         this.previewEl.setTop(ph);
33950         
33951     },
33952     
33953     onMouseDown : function(e)
33954     {   
33955         e.stopEvent();
33956         
33957         this.dragable = true;
33958         this.pinching = false;
33959         
33960         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33961             this.dragable = false;
33962             return;
33963         }
33964         
33965         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33966         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33967         
33968     },
33969     
33970     onMouseMove : function(e)
33971     {   
33972         e.stopEvent();
33973         
33974         if(!this.canvasLoaded){
33975             return;
33976         }
33977         
33978         if (!this.dragable){
33979             return;
33980         }
33981         
33982         var minX = Math.ceil(this.thumbEl.getLeft(true));
33983         var minY = Math.ceil(this.thumbEl.getTop(true));
33984         
33985         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33986         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33987         
33988         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33989         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33990         
33991         x = x - this.mouseX;
33992         y = y - this.mouseY;
33993         
33994         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33995         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33996         
33997         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33998         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33999         
34000         this.previewEl.setLeft(bgX);
34001         this.previewEl.setTop(bgY);
34002         
34003         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34004         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34005     },
34006     
34007     onMouseUp : function(e)
34008     {   
34009         e.stopEvent();
34010         
34011         this.dragable = false;
34012     },
34013     
34014     onMouseWheel : function(e)
34015     {   
34016         e.stopEvent();
34017         
34018         this.startScale = this.scale;
34019         
34020         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34021         
34022         if(!this.zoomable()){
34023             this.scale = this.startScale;
34024             return;
34025         }
34026         
34027         this.draw();
34028         
34029         return;
34030     },
34031     
34032     zoomable : function()
34033     {
34034         var minScale = this.thumbEl.getWidth() / this.minWidth;
34035         
34036         if(this.minWidth < this.minHeight){
34037             minScale = this.thumbEl.getHeight() / this.minHeight;
34038         }
34039         
34040         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34041         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34042         
34043         if(
34044                 this.isDocument &&
34045                 (this.rotate == 0 || this.rotate == 180) && 
34046                 (
34047                     width > this.imageEl.OriginWidth || 
34048                     height > this.imageEl.OriginHeight ||
34049                     (width < this.minWidth && height < this.minHeight)
34050                 )
34051         ){
34052             return false;
34053         }
34054         
34055         if(
34056                 this.isDocument &&
34057                 (this.rotate == 90 || this.rotate == 270) && 
34058                 (
34059                     width > this.imageEl.OriginWidth || 
34060                     height > this.imageEl.OriginHeight ||
34061                     (width < this.minHeight && height < this.minWidth)
34062                 )
34063         ){
34064             return false;
34065         }
34066         
34067         if(
34068                 !this.isDocument &&
34069                 (this.rotate == 0 || this.rotate == 180) && 
34070                 (
34071                     width < this.minWidth || 
34072                     width > this.imageEl.OriginWidth || 
34073                     height < this.minHeight || 
34074                     height > this.imageEl.OriginHeight
34075                 )
34076         ){
34077             return false;
34078         }
34079         
34080         if(
34081                 !this.isDocument &&
34082                 (this.rotate == 90 || this.rotate == 270) && 
34083                 (
34084                     width < this.minHeight || 
34085                     width > this.imageEl.OriginWidth || 
34086                     height < this.minWidth || 
34087                     height > this.imageEl.OriginHeight
34088                 )
34089         ){
34090             return false;
34091         }
34092         
34093         return true;
34094         
34095     },
34096     
34097     onRotateLeft : function(e)
34098     {   
34099         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34100             
34101             var minScale = this.thumbEl.getWidth() / this.minWidth;
34102             
34103             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34104             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34105             
34106             this.startScale = this.scale;
34107             
34108             while (this.getScaleLevel() < minScale){
34109             
34110                 this.scale = this.scale + 1;
34111                 
34112                 if(!this.zoomable()){
34113                     break;
34114                 }
34115                 
34116                 if(
34117                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34118                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34119                 ){
34120                     continue;
34121                 }
34122                 
34123                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34124
34125                 this.draw();
34126                 
34127                 return;
34128             }
34129             
34130             this.scale = this.startScale;
34131             
34132             this.onRotateFail();
34133             
34134             return false;
34135         }
34136         
34137         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34138
34139         if(this.isDocument){
34140             this.setThumbBoxSize();
34141             this.setThumbBoxPosition();
34142             this.setCanvasPosition();
34143         }
34144         
34145         this.draw();
34146         
34147         this.fireEvent('rotate', this, 'left');
34148         
34149     },
34150     
34151     onRotateRight : function(e)
34152     {
34153         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34154             
34155             var minScale = this.thumbEl.getWidth() / this.minWidth;
34156         
34157             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34158             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34159             
34160             this.startScale = this.scale;
34161             
34162             while (this.getScaleLevel() < minScale){
34163             
34164                 this.scale = this.scale + 1;
34165                 
34166                 if(!this.zoomable()){
34167                     break;
34168                 }
34169                 
34170                 if(
34171                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34172                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34173                 ){
34174                     continue;
34175                 }
34176                 
34177                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34178
34179                 this.draw();
34180                 
34181                 return;
34182             }
34183             
34184             this.scale = this.startScale;
34185             
34186             this.onRotateFail();
34187             
34188             return false;
34189         }
34190         
34191         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34192
34193         if(this.isDocument){
34194             this.setThumbBoxSize();
34195             this.setThumbBoxPosition();
34196             this.setCanvasPosition();
34197         }
34198         
34199         this.draw();
34200         
34201         this.fireEvent('rotate', this, 'right');
34202     },
34203     
34204     onRotateFail : function()
34205     {
34206         this.errorEl.show(true);
34207         
34208         var _this = this;
34209         
34210         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34211     },
34212     
34213     draw : function()
34214     {
34215         this.previewEl.dom.innerHTML = '';
34216         
34217         var canvasEl = document.createElement("canvas");
34218         
34219         var contextEl = canvasEl.getContext("2d");
34220         
34221         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34222         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34223         var center = this.imageEl.OriginWidth / 2;
34224         
34225         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34226             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34227             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34228             center = this.imageEl.OriginHeight / 2;
34229         }
34230         
34231         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34232         
34233         contextEl.translate(center, center);
34234         contextEl.rotate(this.rotate * Math.PI / 180);
34235
34236         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34237         
34238         this.canvasEl = document.createElement("canvas");
34239         
34240         this.contextEl = this.canvasEl.getContext("2d");
34241         
34242         switch (this.rotate) {
34243             case 0 :
34244                 
34245                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34246                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34247                 
34248                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34249                 
34250                 break;
34251             case 90 : 
34252                 
34253                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34254                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34255                 
34256                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34257                     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);
34258                     break;
34259                 }
34260                 
34261                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34262                 
34263                 break;
34264             case 180 :
34265                 
34266                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34267                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34268                 
34269                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34270                     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);
34271                     break;
34272                 }
34273                 
34274                 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);
34275                 
34276                 break;
34277             case 270 :
34278                 
34279                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34280                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34281         
34282                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34283                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34284                     break;
34285                 }
34286                 
34287                 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);
34288                 
34289                 break;
34290             default : 
34291                 break;
34292         }
34293         
34294         this.previewEl.appendChild(this.canvasEl);
34295         
34296         this.setCanvasPosition();
34297     },
34298     
34299     crop : function()
34300     {
34301         if(!this.canvasLoaded){
34302             return;
34303         }
34304         
34305         var imageCanvas = document.createElement("canvas");
34306         
34307         var imageContext = imageCanvas.getContext("2d");
34308         
34309         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34310         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34311         
34312         var center = imageCanvas.width / 2;
34313         
34314         imageContext.translate(center, center);
34315         
34316         imageContext.rotate(this.rotate * Math.PI / 180);
34317         
34318         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34319         
34320         var canvas = document.createElement("canvas");
34321         
34322         var context = canvas.getContext("2d");
34323                 
34324         canvas.width = this.minWidth;
34325         canvas.height = this.minHeight;
34326
34327         switch (this.rotate) {
34328             case 0 :
34329                 
34330                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34331                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34332                 
34333                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34334                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34335                 
34336                 var targetWidth = this.minWidth - 2 * x;
34337                 var targetHeight = this.minHeight - 2 * y;
34338                 
34339                 var scale = 1;
34340                 
34341                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34342                     scale = targetWidth / width;
34343                 }
34344                 
34345                 if(x > 0 && y == 0){
34346                     scale = targetHeight / height;
34347                 }
34348                 
34349                 if(x > 0 && y > 0){
34350                     scale = targetWidth / width;
34351                     
34352                     if(width < height){
34353                         scale = targetHeight / height;
34354                     }
34355                 }
34356                 
34357                 context.scale(scale, scale);
34358                 
34359                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34360                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34361
34362                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34363                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34364
34365                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34366                 
34367                 break;
34368             case 90 : 
34369                 
34370                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34371                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34372                 
34373                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34374                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34375                 
34376                 var targetWidth = this.minWidth - 2 * x;
34377                 var targetHeight = this.minHeight - 2 * y;
34378                 
34379                 var scale = 1;
34380                 
34381                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34382                     scale = targetWidth / width;
34383                 }
34384                 
34385                 if(x > 0 && y == 0){
34386                     scale = targetHeight / height;
34387                 }
34388                 
34389                 if(x > 0 && y > 0){
34390                     scale = targetWidth / width;
34391                     
34392                     if(width < height){
34393                         scale = targetHeight / height;
34394                     }
34395                 }
34396                 
34397                 context.scale(scale, scale);
34398                 
34399                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34400                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34401
34402                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34403                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34404                 
34405                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34406                 
34407                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34408                 
34409                 break;
34410             case 180 :
34411                 
34412                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34413                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34414                 
34415                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34416                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34417                 
34418                 var targetWidth = this.minWidth - 2 * x;
34419                 var targetHeight = this.minHeight - 2 * y;
34420                 
34421                 var scale = 1;
34422                 
34423                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34424                     scale = targetWidth / width;
34425                 }
34426                 
34427                 if(x > 0 && y == 0){
34428                     scale = targetHeight / height;
34429                 }
34430                 
34431                 if(x > 0 && y > 0){
34432                     scale = targetWidth / width;
34433                     
34434                     if(width < height){
34435                         scale = targetHeight / height;
34436                     }
34437                 }
34438                 
34439                 context.scale(scale, scale);
34440                 
34441                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34442                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34443
34444                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34445                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34446
34447                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34448                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34449                 
34450                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34451                 
34452                 break;
34453             case 270 :
34454                 
34455                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34456                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34457                 
34458                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34459                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34460                 
34461                 var targetWidth = this.minWidth - 2 * x;
34462                 var targetHeight = this.minHeight - 2 * y;
34463                 
34464                 var scale = 1;
34465                 
34466                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34467                     scale = targetWidth / width;
34468                 }
34469                 
34470                 if(x > 0 && y == 0){
34471                     scale = targetHeight / height;
34472                 }
34473                 
34474                 if(x > 0 && y > 0){
34475                     scale = targetWidth / width;
34476                     
34477                     if(width < height){
34478                         scale = targetHeight / height;
34479                     }
34480                 }
34481                 
34482                 context.scale(scale, scale);
34483                 
34484                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34485                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34486
34487                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34488                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34489                 
34490                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34491                 
34492                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34493                 
34494                 break;
34495             default : 
34496                 break;
34497         }
34498         
34499         this.cropData = canvas.toDataURL(this.cropType);
34500         
34501         if(this.fireEvent('crop', this, this.cropData) !== false){
34502             this.process(this.file, this.cropData);
34503         }
34504         
34505         return;
34506         
34507     },
34508     
34509     setThumbBoxSize : function()
34510     {
34511         var width, height;
34512         
34513         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34514             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34515             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34516             
34517             this.minWidth = width;
34518             this.minHeight = height;
34519             
34520             if(this.rotate == 90 || this.rotate == 270){
34521                 this.minWidth = height;
34522                 this.minHeight = width;
34523             }
34524         }
34525         
34526         height = 300;
34527         width = Math.ceil(this.minWidth * height / this.minHeight);
34528         
34529         if(this.minWidth > this.minHeight){
34530             width = 300;
34531             height = Math.ceil(this.minHeight * width / this.minWidth);
34532         }
34533         
34534         this.thumbEl.setStyle({
34535             width : width + 'px',
34536             height : height + 'px'
34537         });
34538
34539         return;
34540             
34541     },
34542     
34543     setThumbBoxPosition : function()
34544     {
34545         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34546         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34547         
34548         this.thumbEl.setLeft(x);
34549         this.thumbEl.setTop(y);
34550         
34551     },
34552     
34553     baseRotateLevel : function()
34554     {
34555         this.baseRotate = 1;
34556         
34557         if(
34558                 typeof(this.exif) != 'undefined' &&
34559                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34560                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34561         ){
34562             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34563         }
34564         
34565         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34566         
34567     },
34568     
34569     baseScaleLevel : function()
34570     {
34571         var width, height;
34572         
34573         if(this.isDocument){
34574             
34575             if(this.baseRotate == 6 || this.baseRotate == 8){
34576             
34577                 height = this.thumbEl.getHeight();
34578                 this.baseScale = height / this.imageEl.OriginWidth;
34579
34580                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34581                     width = this.thumbEl.getWidth();
34582                     this.baseScale = width / this.imageEl.OriginHeight;
34583                 }
34584
34585                 return;
34586             }
34587
34588             height = this.thumbEl.getHeight();
34589             this.baseScale = height / this.imageEl.OriginHeight;
34590
34591             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34592                 width = this.thumbEl.getWidth();
34593                 this.baseScale = width / this.imageEl.OriginWidth;
34594             }
34595
34596             return;
34597         }
34598         
34599         if(this.baseRotate == 6 || this.baseRotate == 8){
34600             
34601             width = this.thumbEl.getHeight();
34602             this.baseScale = width / this.imageEl.OriginHeight;
34603             
34604             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34605                 height = this.thumbEl.getWidth();
34606                 this.baseScale = height / this.imageEl.OriginHeight;
34607             }
34608             
34609             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34610                 height = this.thumbEl.getWidth();
34611                 this.baseScale = height / this.imageEl.OriginHeight;
34612                 
34613                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34614                     width = this.thumbEl.getHeight();
34615                     this.baseScale = width / this.imageEl.OriginWidth;
34616                 }
34617             }
34618             
34619             return;
34620         }
34621         
34622         width = this.thumbEl.getWidth();
34623         this.baseScale = width / this.imageEl.OriginWidth;
34624         
34625         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34626             height = this.thumbEl.getHeight();
34627             this.baseScale = height / this.imageEl.OriginHeight;
34628         }
34629         
34630         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34631             
34632             height = this.thumbEl.getHeight();
34633             this.baseScale = height / this.imageEl.OriginHeight;
34634             
34635             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34636                 width = this.thumbEl.getWidth();
34637                 this.baseScale = width / this.imageEl.OriginWidth;
34638             }
34639             
34640         }
34641         
34642         return;
34643     },
34644     
34645     getScaleLevel : function()
34646     {
34647         return this.baseScale * Math.pow(1.1, this.scale);
34648     },
34649     
34650     onTouchStart : function(e)
34651     {
34652         if(!this.canvasLoaded){
34653             this.beforeSelectFile(e);
34654             return;
34655         }
34656         
34657         var touches = e.browserEvent.touches;
34658         
34659         if(!touches){
34660             return;
34661         }
34662         
34663         if(touches.length == 1){
34664             this.onMouseDown(e);
34665             return;
34666         }
34667         
34668         if(touches.length != 2){
34669             return;
34670         }
34671         
34672         var coords = [];
34673         
34674         for(var i = 0, finger; finger = touches[i]; i++){
34675             coords.push(finger.pageX, finger.pageY);
34676         }
34677         
34678         var x = Math.pow(coords[0] - coords[2], 2);
34679         var y = Math.pow(coords[1] - coords[3], 2);
34680         
34681         this.startDistance = Math.sqrt(x + y);
34682         
34683         this.startScale = this.scale;
34684         
34685         this.pinching = true;
34686         this.dragable = false;
34687         
34688     },
34689     
34690     onTouchMove : function(e)
34691     {
34692         if(!this.pinching && !this.dragable){
34693             return;
34694         }
34695         
34696         var touches = e.browserEvent.touches;
34697         
34698         if(!touches){
34699             return;
34700         }
34701         
34702         if(this.dragable){
34703             this.onMouseMove(e);
34704             return;
34705         }
34706         
34707         var coords = [];
34708         
34709         for(var i = 0, finger; finger = touches[i]; i++){
34710             coords.push(finger.pageX, finger.pageY);
34711         }
34712         
34713         var x = Math.pow(coords[0] - coords[2], 2);
34714         var y = Math.pow(coords[1] - coords[3], 2);
34715         
34716         this.endDistance = Math.sqrt(x + y);
34717         
34718         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34719         
34720         if(!this.zoomable()){
34721             this.scale = this.startScale;
34722             return;
34723         }
34724         
34725         this.draw();
34726         
34727     },
34728     
34729     onTouchEnd : function(e)
34730     {
34731         this.pinching = false;
34732         this.dragable = false;
34733         
34734     },
34735     
34736     process : function(file, crop)
34737     {
34738         if(this.loadMask){
34739             this.maskEl.mask(this.loadingText);
34740         }
34741         
34742         this.xhr = new XMLHttpRequest();
34743         
34744         file.xhr = this.xhr;
34745
34746         this.xhr.open(this.method, this.url, true);
34747         
34748         var headers = {
34749             "Accept": "application/json",
34750             "Cache-Control": "no-cache",
34751             "X-Requested-With": "XMLHttpRequest"
34752         };
34753         
34754         for (var headerName in headers) {
34755             var headerValue = headers[headerName];
34756             if (headerValue) {
34757                 this.xhr.setRequestHeader(headerName, headerValue);
34758             }
34759         }
34760         
34761         var _this = this;
34762         
34763         this.xhr.onload = function()
34764         {
34765             _this.xhrOnLoad(_this.xhr);
34766         }
34767         
34768         this.xhr.onerror = function()
34769         {
34770             _this.xhrOnError(_this.xhr);
34771         }
34772         
34773         var formData = new FormData();
34774
34775         formData.append('returnHTML', 'NO');
34776         
34777         if(crop){
34778             formData.append('crop', crop);
34779         }
34780         
34781         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34782             formData.append(this.paramName, file, file.name);
34783         }
34784         
34785         if(typeof(file.filename) != 'undefined'){
34786             formData.append('filename', file.filename);
34787         }
34788         
34789         if(typeof(file.mimetype) != 'undefined'){
34790             formData.append('mimetype', file.mimetype);
34791         }
34792         
34793         if(this.fireEvent('arrange', this, formData) != false){
34794             this.xhr.send(formData);
34795         };
34796     },
34797     
34798     xhrOnLoad : function(xhr)
34799     {
34800         if(this.loadMask){
34801             this.maskEl.unmask();
34802         }
34803         
34804         if (xhr.readyState !== 4) {
34805             this.fireEvent('exception', this, xhr);
34806             return;
34807         }
34808
34809         var response = Roo.decode(xhr.responseText);
34810         
34811         if(!response.success){
34812             this.fireEvent('exception', this, xhr);
34813             return;
34814         }
34815         
34816         var response = Roo.decode(xhr.responseText);
34817         
34818         this.fireEvent('upload', this, response);
34819         
34820     },
34821     
34822     xhrOnError : function()
34823     {
34824         if(this.loadMask){
34825             this.maskEl.unmask();
34826         }
34827         
34828         Roo.log('xhr on error');
34829         
34830         var response = Roo.decode(xhr.responseText);
34831           
34832         Roo.log(response);
34833         
34834     },
34835     
34836     prepare : function(file)
34837     {   
34838         if(this.loadMask){
34839             this.maskEl.mask(this.loadingText);
34840         }
34841         
34842         this.file = false;
34843         this.exif = {};
34844         
34845         if(typeof(file) === 'string'){
34846             this.loadCanvas(file);
34847             return;
34848         }
34849         
34850         if(!file || !this.urlAPI){
34851             return;
34852         }
34853         
34854         this.file = file;
34855         this.cropType = file.type;
34856         
34857         var _this = this;
34858         
34859         if(this.fireEvent('prepare', this, this.file) != false){
34860             
34861             var reader = new FileReader();
34862             
34863             reader.onload = function (e) {
34864                 if (e.target.error) {
34865                     Roo.log(e.target.error);
34866                     return;
34867                 }
34868                 
34869                 var buffer = e.target.result,
34870                     dataView = new DataView(buffer),
34871                     offset = 2,
34872                     maxOffset = dataView.byteLength - 4,
34873                     markerBytes,
34874                     markerLength;
34875                 
34876                 if (dataView.getUint16(0) === 0xffd8) {
34877                     while (offset < maxOffset) {
34878                         markerBytes = dataView.getUint16(offset);
34879                         
34880                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34881                             markerLength = dataView.getUint16(offset + 2) + 2;
34882                             if (offset + markerLength > dataView.byteLength) {
34883                                 Roo.log('Invalid meta data: Invalid segment size.');
34884                                 break;
34885                             }
34886                             
34887                             if(markerBytes == 0xffe1){
34888                                 _this.parseExifData(
34889                                     dataView,
34890                                     offset,
34891                                     markerLength
34892                                 );
34893                             }
34894                             
34895                             offset += markerLength;
34896                             
34897                             continue;
34898                         }
34899                         
34900                         break;
34901                     }
34902                     
34903                 }
34904                 
34905                 var url = _this.urlAPI.createObjectURL(_this.file);
34906                 
34907                 _this.loadCanvas(url);
34908                 
34909                 return;
34910             }
34911             
34912             reader.readAsArrayBuffer(this.file);
34913             
34914         }
34915         
34916     },
34917     
34918     parseExifData : function(dataView, offset, length)
34919     {
34920         var tiffOffset = offset + 10,
34921             littleEndian,
34922             dirOffset;
34923     
34924         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34925             // No Exif data, might be XMP data instead
34926             return;
34927         }
34928         
34929         // Check for the ASCII code for "Exif" (0x45786966):
34930         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34931             // No Exif data, might be XMP data instead
34932             return;
34933         }
34934         if (tiffOffset + 8 > dataView.byteLength) {
34935             Roo.log('Invalid Exif data: Invalid segment size.');
34936             return;
34937         }
34938         // Check for the two null bytes:
34939         if (dataView.getUint16(offset + 8) !== 0x0000) {
34940             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34941             return;
34942         }
34943         // Check the byte alignment:
34944         switch (dataView.getUint16(tiffOffset)) {
34945         case 0x4949:
34946             littleEndian = true;
34947             break;
34948         case 0x4D4D:
34949             littleEndian = false;
34950             break;
34951         default:
34952             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34953             return;
34954         }
34955         // Check for the TIFF tag marker (0x002A):
34956         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34957             Roo.log('Invalid Exif data: Missing TIFF marker.');
34958             return;
34959         }
34960         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34961         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34962         
34963         this.parseExifTags(
34964             dataView,
34965             tiffOffset,
34966             tiffOffset + dirOffset,
34967             littleEndian
34968         );
34969     },
34970     
34971     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34972     {
34973         var tagsNumber,
34974             dirEndOffset,
34975             i;
34976         if (dirOffset + 6 > dataView.byteLength) {
34977             Roo.log('Invalid Exif data: Invalid directory offset.');
34978             return;
34979         }
34980         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34981         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34982         if (dirEndOffset + 4 > dataView.byteLength) {
34983             Roo.log('Invalid Exif data: Invalid directory size.');
34984             return;
34985         }
34986         for (i = 0; i < tagsNumber; i += 1) {
34987             this.parseExifTag(
34988                 dataView,
34989                 tiffOffset,
34990                 dirOffset + 2 + 12 * i, // tag offset
34991                 littleEndian
34992             );
34993         }
34994         // Return the offset to the next directory:
34995         return dataView.getUint32(dirEndOffset, littleEndian);
34996     },
34997     
34998     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34999     {
35000         var tag = dataView.getUint16(offset, littleEndian);
35001         
35002         this.exif[tag] = this.getExifValue(
35003             dataView,
35004             tiffOffset,
35005             offset,
35006             dataView.getUint16(offset + 2, littleEndian), // tag type
35007             dataView.getUint32(offset + 4, littleEndian), // tag length
35008             littleEndian
35009         );
35010     },
35011     
35012     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35013     {
35014         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35015             tagSize,
35016             dataOffset,
35017             values,
35018             i,
35019             str,
35020             c;
35021     
35022         if (!tagType) {
35023             Roo.log('Invalid Exif data: Invalid tag type.');
35024             return;
35025         }
35026         
35027         tagSize = tagType.size * length;
35028         // Determine if the value is contained in the dataOffset bytes,
35029         // or if the value at the dataOffset is a pointer to the actual data:
35030         dataOffset = tagSize > 4 ?
35031                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35032         if (dataOffset + tagSize > dataView.byteLength) {
35033             Roo.log('Invalid Exif data: Invalid data offset.');
35034             return;
35035         }
35036         if (length === 1) {
35037             return tagType.getValue(dataView, dataOffset, littleEndian);
35038         }
35039         values = [];
35040         for (i = 0; i < length; i += 1) {
35041             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35042         }
35043         
35044         if (tagType.ascii) {
35045             str = '';
35046             // Concatenate the chars:
35047             for (i = 0; i < values.length; i += 1) {
35048                 c = values[i];
35049                 // Ignore the terminating NULL byte(s):
35050                 if (c === '\u0000') {
35051                     break;
35052                 }
35053                 str += c;
35054             }
35055             return str;
35056         }
35057         return values;
35058     }
35059     
35060 });
35061
35062 Roo.apply(Roo.bootstrap.UploadCropbox, {
35063     tags : {
35064         'Orientation': 0x0112
35065     },
35066     
35067     Orientation: {
35068             1: 0, //'top-left',
35069 //            2: 'top-right',
35070             3: 180, //'bottom-right',
35071 //            4: 'bottom-left',
35072 //            5: 'left-top',
35073             6: 90, //'right-top',
35074 //            7: 'right-bottom',
35075             8: 270 //'left-bottom'
35076     },
35077     
35078     exifTagTypes : {
35079         // byte, 8-bit unsigned int:
35080         1: {
35081             getValue: function (dataView, dataOffset) {
35082                 return dataView.getUint8(dataOffset);
35083             },
35084             size: 1
35085         },
35086         // ascii, 8-bit byte:
35087         2: {
35088             getValue: function (dataView, dataOffset) {
35089                 return String.fromCharCode(dataView.getUint8(dataOffset));
35090             },
35091             size: 1,
35092             ascii: true
35093         },
35094         // short, 16 bit int:
35095         3: {
35096             getValue: function (dataView, dataOffset, littleEndian) {
35097                 return dataView.getUint16(dataOffset, littleEndian);
35098             },
35099             size: 2
35100         },
35101         // long, 32 bit int:
35102         4: {
35103             getValue: function (dataView, dataOffset, littleEndian) {
35104                 return dataView.getUint32(dataOffset, littleEndian);
35105             },
35106             size: 4
35107         },
35108         // rational = two long values, first is numerator, second is denominator:
35109         5: {
35110             getValue: function (dataView, dataOffset, littleEndian) {
35111                 return dataView.getUint32(dataOffset, littleEndian) /
35112                     dataView.getUint32(dataOffset + 4, littleEndian);
35113             },
35114             size: 8
35115         },
35116         // slong, 32 bit signed int:
35117         9: {
35118             getValue: function (dataView, dataOffset, littleEndian) {
35119                 return dataView.getInt32(dataOffset, littleEndian);
35120             },
35121             size: 4
35122         },
35123         // srational, two slongs, first is numerator, second is denominator:
35124         10: {
35125             getValue: function (dataView, dataOffset, littleEndian) {
35126                 return dataView.getInt32(dataOffset, littleEndian) /
35127                     dataView.getInt32(dataOffset + 4, littleEndian);
35128             },
35129             size: 8
35130         }
35131     },
35132     
35133     footer : {
35134         STANDARD : [
35135             {
35136                 tag : 'div',
35137                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35138                 action : 'rotate-left',
35139                 cn : [
35140                     {
35141                         tag : 'button',
35142                         cls : 'btn btn-default',
35143                         html : '<i class="fa fa-undo"></i>'
35144                     }
35145                 ]
35146             },
35147             {
35148                 tag : 'div',
35149                 cls : 'btn-group roo-upload-cropbox-picture',
35150                 action : 'picture',
35151                 cn : [
35152                     {
35153                         tag : 'button',
35154                         cls : 'btn btn-default',
35155                         html : '<i class="fa fa-picture-o"></i>'
35156                     }
35157                 ]
35158             },
35159             {
35160                 tag : 'div',
35161                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35162                 action : 'rotate-right',
35163                 cn : [
35164                     {
35165                         tag : 'button',
35166                         cls : 'btn btn-default',
35167                         html : '<i class="fa fa-repeat"></i>'
35168                     }
35169                 ]
35170             }
35171         ],
35172         DOCUMENT : [
35173             {
35174                 tag : 'div',
35175                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35176                 action : 'rotate-left',
35177                 cn : [
35178                     {
35179                         tag : 'button',
35180                         cls : 'btn btn-default',
35181                         html : '<i class="fa fa-undo"></i>'
35182                     }
35183                 ]
35184             },
35185             {
35186                 tag : 'div',
35187                 cls : 'btn-group roo-upload-cropbox-download',
35188                 action : 'download',
35189                 cn : [
35190                     {
35191                         tag : 'button',
35192                         cls : 'btn btn-default',
35193                         html : '<i class="fa fa-download"></i>'
35194                     }
35195                 ]
35196             },
35197             {
35198                 tag : 'div',
35199                 cls : 'btn-group roo-upload-cropbox-crop',
35200                 action : 'crop',
35201                 cn : [
35202                     {
35203                         tag : 'button',
35204                         cls : 'btn btn-default',
35205                         html : '<i class="fa fa-crop"></i>'
35206                     }
35207                 ]
35208             },
35209             {
35210                 tag : 'div',
35211                 cls : 'btn-group roo-upload-cropbox-trash',
35212                 action : 'trash',
35213                 cn : [
35214                     {
35215                         tag : 'button',
35216                         cls : 'btn btn-default',
35217                         html : '<i class="fa fa-trash"></i>'
35218                     }
35219                 ]
35220             },
35221             {
35222                 tag : 'div',
35223                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35224                 action : 'rotate-right',
35225                 cn : [
35226                     {
35227                         tag : 'button',
35228                         cls : 'btn btn-default',
35229                         html : '<i class="fa fa-repeat"></i>'
35230                     }
35231                 ]
35232             }
35233         ],
35234         ROTATOR : [
35235             {
35236                 tag : 'div',
35237                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35238                 action : 'rotate-left',
35239                 cn : [
35240                     {
35241                         tag : 'button',
35242                         cls : 'btn btn-default',
35243                         html : '<i class="fa fa-undo"></i>'
35244                     }
35245                 ]
35246             },
35247             {
35248                 tag : 'div',
35249                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35250                 action : 'rotate-right',
35251                 cn : [
35252                     {
35253                         tag : 'button',
35254                         cls : 'btn btn-default',
35255                         html : '<i class="fa fa-repeat"></i>'
35256                     }
35257                 ]
35258             }
35259         ]
35260     }
35261 });
35262
35263 /*
35264 * Licence: LGPL
35265 */
35266
35267 /**
35268  * @class Roo.bootstrap.DocumentManager
35269  * @extends Roo.bootstrap.Component
35270  * Bootstrap DocumentManager class
35271  * @cfg {String} paramName default 'imageUpload'
35272  * @cfg {String} toolTipName default 'filename'
35273  * @cfg {String} method default POST
35274  * @cfg {String} url action url
35275  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35276  * @cfg {Boolean} multiple multiple upload default true
35277  * @cfg {Number} thumbSize default 300
35278  * @cfg {String} fieldLabel
35279  * @cfg {Number} labelWidth default 4
35280  * @cfg {String} labelAlign (left|top) default left
35281  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35282 * @cfg {Number} labellg set the width of label (1-12)
35283  * @cfg {Number} labelmd set the width of label (1-12)
35284  * @cfg {Number} labelsm set the width of label (1-12)
35285  * @cfg {Number} labelxs set the width of label (1-12)
35286  * 
35287  * @constructor
35288  * Create a new DocumentManager
35289  * @param {Object} config The config object
35290  */
35291
35292 Roo.bootstrap.DocumentManager = function(config){
35293     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35294     
35295     this.files = [];
35296     this.delegates = [];
35297     
35298     this.addEvents({
35299         /**
35300          * @event initial
35301          * Fire when initial the DocumentManager
35302          * @param {Roo.bootstrap.DocumentManager} this
35303          */
35304         "initial" : true,
35305         /**
35306          * @event inspect
35307          * inspect selected file
35308          * @param {Roo.bootstrap.DocumentManager} this
35309          * @param {File} file
35310          */
35311         "inspect" : true,
35312         /**
35313          * @event exception
35314          * Fire when xhr load exception
35315          * @param {Roo.bootstrap.DocumentManager} this
35316          * @param {XMLHttpRequest} xhr
35317          */
35318         "exception" : true,
35319         /**
35320          * @event afterupload
35321          * Fire when xhr load exception
35322          * @param {Roo.bootstrap.DocumentManager} this
35323          * @param {XMLHttpRequest} xhr
35324          */
35325         "afterupload" : true,
35326         /**
35327          * @event prepare
35328          * prepare the form data
35329          * @param {Roo.bootstrap.DocumentManager} this
35330          * @param {Object} formData
35331          */
35332         "prepare" : true,
35333         /**
35334          * @event remove
35335          * Fire when remove the file
35336          * @param {Roo.bootstrap.DocumentManager} this
35337          * @param {Object} file
35338          */
35339         "remove" : true,
35340         /**
35341          * @event refresh
35342          * Fire after refresh the file
35343          * @param {Roo.bootstrap.DocumentManager} this
35344          */
35345         "refresh" : true,
35346         /**
35347          * @event click
35348          * Fire after click the image
35349          * @param {Roo.bootstrap.DocumentManager} this
35350          * @param {Object} file
35351          */
35352         "click" : true,
35353         /**
35354          * @event edit
35355          * Fire when upload a image and editable set to true
35356          * @param {Roo.bootstrap.DocumentManager} this
35357          * @param {Object} file
35358          */
35359         "edit" : true,
35360         /**
35361          * @event beforeselectfile
35362          * Fire before select file
35363          * @param {Roo.bootstrap.DocumentManager} this
35364          */
35365         "beforeselectfile" : true,
35366         /**
35367          * @event process
35368          * Fire before process file
35369          * @param {Roo.bootstrap.DocumentManager} this
35370          * @param {Object} file
35371          */
35372         "process" : true,
35373         /**
35374          * @event previewrendered
35375          * Fire when preview rendered
35376          * @param {Roo.bootstrap.DocumentManager} this
35377          * @param {Object} file
35378          */
35379         "previewrendered" : true,
35380         /**
35381          */
35382         "previewResize" : true
35383         
35384     });
35385 };
35386
35387 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35388     
35389     boxes : 0,
35390     inputName : '',
35391     thumbSize : 300,
35392     multiple : true,
35393     files : false,
35394     method : 'POST',
35395     url : '',
35396     paramName : 'imageUpload',
35397     toolTipName : 'filename',
35398     fieldLabel : '',
35399     labelWidth : 4,
35400     labelAlign : 'left',
35401     editable : true,
35402     delegates : false,
35403     xhr : false, 
35404     
35405     labellg : 0,
35406     labelmd : 0,
35407     labelsm : 0,
35408     labelxs : 0,
35409     
35410     getAutoCreate : function()
35411     {   
35412         var managerWidget = {
35413             tag : 'div',
35414             cls : 'roo-document-manager',
35415             cn : [
35416                 {
35417                     tag : 'input',
35418                     cls : 'roo-document-manager-selector',
35419                     type : 'file'
35420                 },
35421                 {
35422                     tag : 'div',
35423                     cls : 'roo-document-manager-uploader',
35424                     cn : [
35425                         {
35426                             tag : 'div',
35427                             cls : 'roo-document-manager-upload-btn',
35428                             html : '<i class="fa fa-plus"></i>'
35429                         }
35430                     ]
35431                     
35432                 }
35433             ]
35434         };
35435         
35436         var content = [
35437             {
35438                 tag : 'div',
35439                 cls : 'column col-md-12',
35440                 cn : managerWidget
35441             }
35442         ];
35443         
35444         if(this.fieldLabel.length){
35445             
35446             content = [
35447                 {
35448                     tag : 'div',
35449                     cls : 'column col-md-12',
35450                     html : this.fieldLabel
35451                 },
35452                 {
35453                     tag : 'div',
35454                     cls : 'column col-md-12',
35455                     cn : managerWidget
35456                 }
35457             ];
35458
35459             if(this.labelAlign == 'left'){
35460                 content = [
35461                     {
35462                         tag : 'div',
35463                         cls : 'column',
35464                         html : this.fieldLabel
35465                     },
35466                     {
35467                         tag : 'div',
35468                         cls : 'column',
35469                         cn : managerWidget
35470                     }
35471                 ];
35472                 
35473                 if(this.labelWidth > 12){
35474                     content[0].style = "width: " + this.labelWidth + 'px';
35475                 }
35476
35477                 if(this.labelWidth < 13 && this.labelmd == 0){
35478                     this.labelmd = this.labelWidth;
35479                 }
35480
35481                 if(this.labellg > 0){
35482                     content[0].cls += ' col-lg-' + this.labellg;
35483                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35484                 }
35485
35486                 if(this.labelmd > 0){
35487                     content[0].cls += ' col-md-' + this.labelmd;
35488                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35489                 }
35490
35491                 if(this.labelsm > 0){
35492                     content[0].cls += ' col-sm-' + this.labelsm;
35493                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35494                 }
35495
35496                 if(this.labelxs > 0){
35497                     content[0].cls += ' col-xs-' + this.labelxs;
35498                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35499                 }
35500                 
35501             }
35502         }
35503         
35504         var cfg = {
35505             tag : 'div',
35506             cls : 'row clearfix',
35507             cn : content
35508         };
35509         
35510         return cfg;
35511         
35512     },
35513     
35514     initEvents : function()
35515     {
35516         this.managerEl = this.el.select('.roo-document-manager', true).first();
35517         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35518         
35519         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35520         this.selectorEl.hide();
35521         
35522         if(this.multiple){
35523             this.selectorEl.attr('multiple', 'multiple');
35524         }
35525         
35526         this.selectorEl.on('change', this.onFileSelected, this);
35527         
35528         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35529         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35530         
35531         this.uploader.on('click', this.onUploaderClick, this);
35532         
35533         this.renderProgressDialog();
35534         
35535         var _this = this;
35536         
35537         window.addEventListener("resize", function() { _this.refresh(); } );
35538         
35539         this.fireEvent('initial', this);
35540     },
35541     
35542     renderProgressDialog : function()
35543     {
35544         var _this = this;
35545         
35546         this.progressDialog = new Roo.bootstrap.Modal({
35547             cls : 'roo-document-manager-progress-dialog',
35548             allow_close : false,
35549             animate : false,
35550             title : '',
35551             buttons : [
35552                 {
35553                     name  :'cancel',
35554                     weight : 'danger',
35555                     html : 'Cancel'
35556                 }
35557             ], 
35558             listeners : { 
35559                 btnclick : function() {
35560                     _this.uploadCancel();
35561                     this.hide();
35562                 }
35563             }
35564         });
35565          
35566         this.progressDialog.render(Roo.get(document.body));
35567          
35568         this.progress = new Roo.bootstrap.Progress({
35569             cls : 'roo-document-manager-progress',
35570             active : true,
35571             striped : true
35572         });
35573         
35574         this.progress.render(this.progressDialog.getChildContainer());
35575         
35576         this.progressBar = new Roo.bootstrap.ProgressBar({
35577             cls : 'roo-document-manager-progress-bar',
35578             aria_valuenow : 0,
35579             aria_valuemin : 0,
35580             aria_valuemax : 12,
35581             panel : 'success'
35582         });
35583         
35584         this.progressBar.render(this.progress.getChildContainer());
35585     },
35586     
35587     onUploaderClick : function(e)
35588     {
35589         e.preventDefault();
35590      
35591         if(this.fireEvent('beforeselectfile', this) != false){
35592             this.selectorEl.dom.click();
35593         }
35594         
35595     },
35596     
35597     onFileSelected : function(e)
35598     {
35599         e.preventDefault();
35600         
35601         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35602             return;
35603         }
35604         
35605         Roo.each(this.selectorEl.dom.files, function(file){
35606             if(this.fireEvent('inspect', this, file) != false){
35607                 this.files.push(file);
35608             }
35609         }, this);
35610         
35611         this.queue();
35612         
35613     },
35614     
35615     queue : function()
35616     {
35617         this.selectorEl.dom.value = '';
35618         
35619         if(!this.files || !this.files.length){
35620             return;
35621         }
35622         
35623         if(this.boxes > 0 && this.files.length > this.boxes){
35624             this.files = this.files.slice(0, this.boxes);
35625         }
35626         
35627         this.uploader.show();
35628         
35629         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35630             this.uploader.hide();
35631         }
35632         
35633         var _this = this;
35634         
35635         var files = [];
35636         
35637         var docs = [];
35638         
35639         Roo.each(this.files, function(file){
35640             
35641             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35642                 var f = this.renderPreview(file);
35643                 files.push(f);
35644                 return;
35645             }
35646             
35647             if(file.type.indexOf('image') != -1){
35648                 this.delegates.push(
35649                     (function(){
35650                         _this.process(file);
35651                     }).createDelegate(this)
35652                 );
35653         
35654                 return;
35655             }
35656             
35657             docs.push(
35658                 (function(){
35659                     _this.process(file);
35660                 }).createDelegate(this)
35661             );
35662             
35663         }, this);
35664         
35665         this.files = files;
35666         
35667         this.delegates = this.delegates.concat(docs);
35668         
35669         if(!this.delegates.length){
35670             this.refresh();
35671             return;
35672         }
35673         
35674         this.progressBar.aria_valuemax = this.delegates.length;
35675         
35676         this.arrange();
35677         
35678         return;
35679     },
35680     
35681     arrange : function()
35682     {
35683         if(!this.delegates.length){
35684             this.progressDialog.hide();
35685             this.refresh();
35686             return;
35687         }
35688         
35689         var delegate = this.delegates.shift();
35690         
35691         this.progressDialog.show();
35692         
35693         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35694         
35695         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35696         
35697         delegate();
35698     },
35699     
35700     refresh : function()
35701     {
35702         this.uploader.show();
35703         
35704         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35705             this.uploader.hide();
35706         }
35707         
35708         Roo.isTouch ? this.closable(false) : this.closable(true);
35709         
35710         this.fireEvent('refresh', this);
35711     },
35712     
35713     onRemove : function(e, el, o)
35714     {
35715         e.preventDefault();
35716         
35717         this.fireEvent('remove', this, o);
35718         
35719     },
35720     
35721     remove : function(o)
35722     {
35723         var files = [];
35724         
35725         Roo.each(this.files, function(file){
35726             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35727                 files.push(file);
35728                 return;
35729             }
35730
35731             o.target.remove();
35732
35733         }, this);
35734         
35735         this.files = files;
35736         
35737         this.refresh();
35738     },
35739     
35740     clear : function()
35741     {
35742         Roo.each(this.files, function(file){
35743             if(!file.target){
35744                 return;
35745             }
35746             
35747             file.target.remove();
35748
35749         }, this);
35750         
35751         this.files = [];
35752         
35753         this.refresh();
35754     },
35755     
35756     onClick : function(e, el, o)
35757     {
35758         e.preventDefault();
35759         
35760         this.fireEvent('click', this, o);
35761         
35762     },
35763     
35764     closable : function(closable)
35765     {
35766         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35767             
35768             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35769             
35770             if(closable){
35771                 el.show();
35772                 return;
35773             }
35774             
35775             el.hide();
35776             
35777         }, this);
35778     },
35779     
35780     xhrOnLoad : function(xhr)
35781     {
35782         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35783             el.remove();
35784         }, this);
35785         
35786         if (xhr.readyState !== 4) {
35787             this.arrange();
35788             this.fireEvent('exception', this, xhr);
35789             return;
35790         }
35791
35792         var response = Roo.decode(xhr.responseText);
35793         
35794         if(!response.success){
35795             this.arrange();
35796             this.fireEvent('exception', this, xhr);
35797             return;
35798         }
35799         
35800         var file = this.renderPreview(response.data);
35801         
35802         this.files.push(file);
35803         
35804         this.arrange();
35805         
35806         this.fireEvent('afterupload', this, xhr);
35807         
35808     },
35809     
35810     xhrOnError : function(xhr)
35811     {
35812         Roo.log('xhr on error');
35813         
35814         var response = Roo.decode(xhr.responseText);
35815           
35816         Roo.log(response);
35817         
35818         this.arrange();
35819     },
35820     
35821     process : function(file)
35822     {
35823         if(this.fireEvent('process', this, file) !== false){
35824             if(this.editable && file.type.indexOf('image') != -1){
35825                 this.fireEvent('edit', this, file);
35826                 return;
35827             }
35828
35829             this.uploadStart(file, false);
35830
35831             return;
35832         }
35833         
35834     },
35835     
35836     uploadStart : function(file, crop)
35837     {
35838         this.xhr = new XMLHttpRequest();
35839         
35840         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35841             this.arrange();
35842             return;
35843         }
35844         
35845         file.xhr = this.xhr;
35846             
35847         this.managerEl.createChild({
35848             tag : 'div',
35849             cls : 'roo-document-manager-loading',
35850             cn : [
35851                 {
35852                     tag : 'div',
35853                     tooltip : file.name,
35854                     cls : 'roo-document-manager-thumb',
35855                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35856                 }
35857             ]
35858
35859         });
35860
35861         this.xhr.open(this.method, this.url, true);
35862         
35863         var headers = {
35864             "Accept": "application/json",
35865             "Cache-Control": "no-cache",
35866             "X-Requested-With": "XMLHttpRequest"
35867         };
35868         
35869         for (var headerName in headers) {
35870             var headerValue = headers[headerName];
35871             if (headerValue) {
35872                 this.xhr.setRequestHeader(headerName, headerValue);
35873             }
35874         }
35875         
35876         var _this = this;
35877         
35878         this.xhr.onload = function()
35879         {
35880             _this.xhrOnLoad(_this.xhr);
35881         }
35882         
35883         this.xhr.onerror = function()
35884         {
35885             _this.xhrOnError(_this.xhr);
35886         }
35887         
35888         var formData = new FormData();
35889
35890         formData.append('returnHTML', 'NO');
35891         
35892         if(crop){
35893             formData.append('crop', crop);
35894         }
35895         
35896         formData.append(this.paramName, file, file.name);
35897         
35898         var options = {
35899             file : file, 
35900             manually : false
35901         };
35902         
35903         if(this.fireEvent('prepare', this, formData, options) != false){
35904             
35905             if(options.manually){
35906                 return;
35907             }
35908             
35909             this.xhr.send(formData);
35910             return;
35911         };
35912         
35913         this.uploadCancel();
35914     },
35915     
35916     uploadCancel : function()
35917     {
35918         if (this.xhr) {
35919             this.xhr.abort();
35920         }
35921         
35922         this.delegates = [];
35923         
35924         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35925             el.remove();
35926         }, this);
35927         
35928         this.arrange();
35929     },
35930     
35931     renderPreview : function(file)
35932     {
35933         if(typeof(file.target) != 'undefined' && file.target){
35934             return file;
35935         }
35936         
35937         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35938         
35939         var previewEl = this.managerEl.createChild({
35940             tag : 'div',
35941             cls : 'roo-document-manager-preview',
35942             cn : [
35943                 {
35944                     tag : 'div',
35945                     tooltip : file[this.toolTipName],
35946                     cls : 'roo-document-manager-thumb',
35947                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35948                 },
35949                 {
35950                     tag : 'button',
35951                     cls : 'close',
35952                     html : '<i class="fa fa-times-circle"></i>'
35953                 }
35954             ]
35955         });
35956
35957         var close = previewEl.select('button.close', true).first();
35958
35959         close.on('click', this.onRemove, this, file);
35960
35961         file.target = previewEl;
35962
35963         var image = previewEl.select('img', true).first();
35964         
35965         var _this = this;
35966         
35967         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35968         
35969         image.on('click', this.onClick, this, file);
35970         
35971         this.fireEvent('previewrendered', this, file);
35972         
35973         return file;
35974         
35975     },
35976     
35977     onPreviewLoad : function(file, image)
35978     {
35979         if(typeof(file.target) == 'undefined' || !file.target){
35980             return;
35981         }
35982         
35983         var width = image.dom.naturalWidth || image.dom.width;
35984         var height = image.dom.naturalHeight || image.dom.height;
35985         
35986         if(!this.previewResize) {
35987             return;
35988         }
35989         
35990         if(width > height){
35991             file.target.addClass('wide');
35992             return;
35993         }
35994         
35995         file.target.addClass('tall');
35996         return;
35997         
35998     },
35999     
36000     uploadFromSource : function(file, crop)
36001     {
36002         this.xhr = new XMLHttpRequest();
36003         
36004         this.managerEl.createChild({
36005             tag : 'div',
36006             cls : 'roo-document-manager-loading',
36007             cn : [
36008                 {
36009                     tag : 'div',
36010                     tooltip : file.name,
36011                     cls : 'roo-document-manager-thumb',
36012                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36013                 }
36014             ]
36015
36016         });
36017
36018         this.xhr.open(this.method, this.url, true);
36019         
36020         var headers = {
36021             "Accept": "application/json",
36022             "Cache-Control": "no-cache",
36023             "X-Requested-With": "XMLHttpRequest"
36024         };
36025         
36026         for (var headerName in headers) {
36027             var headerValue = headers[headerName];
36028             if (headerValue) {
36029                 this.xhr.setRequestHeader(headerName, headerValue);
36030             }
36031         }
36032         
36033         var _this = this;
36034         
36035         this.xhr.onload = function()
36036         {
36037             _this.xhrOnLoad(_this.xhr);
36038         }
36039         
36040         this.xhr.onerror = function()
36041         {
36042             _this.xhrOnError(_this.xhr);
36043         }
36044         
36045         var formData = new FormData();
36046
36047         formData.append('returnHTML', 'NO');
36048         
36049         formData.append('crop', crop);
36050         
36051         if(typeof(file.filename) != 'undefined'){
36052             formData.append('filename', file.filename);
36053         }
36054         
36055         if(typeof(file.mimetype) != 'undefined'){
36056             formData.append('mimetype', file.mimetype);
36057         }
36058         
36059         Roo.log(formData);
36060         
36061         if(this.fireEvent('prepare', this, formData) != false){
36062             this.xhr.send(formData);
36063         };
36064     }
36065 });
36066
36067 /*
36068 * Licence: LGPL
36069 */
36070
36071 /**
36072  * @class Roo.bootstrap.DocumentViewer
36073  * @extends Roo.bootstrap.Component
36074  * Bootstrap DocumentViewer class
36075  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36076  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36077  * 
36078  * @constructor
36079  * Create a new DocumentViewer
36080  * @param {Object} config The config object
36081  */
36082
36083 Roo.bootstrap.DocumentViewer = function(config){
36084     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36085     
36086     this.addEvents({
36087         /**
36088          * @event initial
36089          * Fire after initEvent
36090          * @param {Roo.bootstrap.DocumentViewer} this
36091          */
36092         "initial" : true,
36093         /**
36094          * @event click
36095          * Fire after click
36096          * @param {Roo.bootstrap.DocumentViewer} this
36097          */
36098         "click" : true,
36099         /**
36100          * @event download
36101          * Fire after download button
36102          * @param {Roo.bootstrap.DocumentViewer} this
36103          */
36104         "download" : true,
36105         /**
36106          * @event trash
36107          * Fire after trash button
36108          * @param {Roo.bootstrap.DocumentViewer} this
36109          */
36110         "trash" : true
36111         
36112     });
36113 };
36114
36115 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36116     
36117     showDownload : true,
36118     
36119     showTrash : true,
36120     
36121     getAutoCreate : function()
36122     {
36123         var cfg = {
36124             tag : 'div',
36125             cls : 'roo-document-viewer',
36126             cn : [
36127                 {
36128                     tag : 'div',
36129                     cls : 'roo-document-viewer-body',
36130                     cn : [
36131                         {
36132                             tag : 'div',
36133                             cls : 'roo-document-viewer-thumb',
36134                             cn : [
36135                                 {
36136                                     tag : 'img',
36137                                     cls : 'roo-document-viewer-image'
36138                                 }
36139                             ]
36140                         }
36141                     ]
36142                 },
36143                 {
36144                     tag : 'div',
36145                     cls : 'roo-document-viewer-footer',
36146                     cn : {
36147                         tag : 'div',
36148                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36149                         cn : [
36150                             {
36151                                 tag : 'div',
36152                                 cls : 'btn-group roo-document-viewer-download',
36153                                 cn : [
36154                                     {
36155                                         tag : 'button',
36156                                         cls : 'btn btn-default',
36157                                         html : '<i class="fa fa-download"></i>'
36158                                     }
36159                                 ]
36160                             },
36161                             {
36162                                 tag : 'div',
36163                                 cls : 'btn-group roo-document-viewer-trash',
36164                                 cn : [
36165                                     {
36166                                         tag : 'button',
36167                                         cls : 'btn btn-default',
36168                                         html : '<i class="fa fa-trash"></i>'
36169                                     }
36170                                 ]
36171                             }
36172                         ]
36173                     }
36174                 }
36175             ]
36176         };
36177         
36178         return cfg;
36179     },
36180     
36181     initEvents : function()
36182     {
36183         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36184         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36185         
36186         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36187         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36188         
36189         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36190         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36191         
36192         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36193         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36194         
36195         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36196         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36197         
36198         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36199         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36200         
36201         this.bodyEl.on('click', this.onClick, this);
36202         this.downloadBtn.on('click', this.onDownload, this);
36203         this.trashBtn.on('click', this.onTrash, this);
36204         
36205         this.downloadBtn.hide();
36206         this.trashBtn.hide();
36207         
36208         if(this.showDownload){
36209             this.downloadBtn.show();
36210         }
36211         
36212         if(this.showTrash){
36213             this.trashBtn.show();
36214         }
36215         
36216         if(!this.showDownload && !this.showTrash) {
36217             this.footerEl.hide();
36218         }
36219         
36220     },
36221     
36222     initial : function()
36223     {
36224         this.fireEvent('initial', this);
36225         
36226     },
36227     
36228     onClick : function(e)
36229     {
36230         e.preventDefault();
36231         
36232         this.fireEvent('click', this);
36233     },
36234     
36235     onDownload : function(e)
36236     {
36237         e.preventDefault();
36238         
36239         this.fireEvent('download', this);
36240     },
36241     
36242     onTrash : function(e)
36243     {
36244         e.preventDefault();
36245         
36246         this.fireEvent('trash', this);
36247     }
36248     
36249 });
36250 /*
36251  * - LGPL
36252  *
36253  * FieldLabel
36254  * 
36255  */
36256
36257 /**
36258  * @class Roo.bootstrap.form.FieldLabel
36259  * @extends Roo.bootstrap.Component
36260  * Bootstrap FieldLabel class
36261  * @cfg {String} html contents of the element
36262  * @cfg {String} tag tag of the element default label
36263  * @cfg {String} cls class of the element
36264  * @cfg {String} target label target 
36265  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36266  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36267  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36268  * @cfg {String} iconTooltip default "This field is required"
36269  * @cfg {String} indicatorpos (left|right) default left
36270  * 
36271  * @constructor
36272  * Create a new FieldLabel
36273  * @param {Object} config The config object
36274  */
36275
36276 Roo.bootstrap.form.FieldLabel = function(config){
36277     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36278     
36279     this.addEvents({
36280             /**
36281              * @event invalid
36282              * Fires after the field has been marked as invalid.
36283              * @param {Roo.form.FieldLabel} this
36284              * @param {String} msg The validation message
36285              */
36286             invalid : true,
36287             /**
36288              * @event valid
36289              * Fires after the field has been validated with no errors.
36290              * @param {Roo.form.FieldLabel} this
36291              */
36292             valid : true
36293         });
36294 };
36295
36296 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36297     
36298     tag: 'label',
36299     cls: '',
36300     html: '',
36301     target: '',
36302     allowBlank : true,
36303     invalidClass : 'has-warning',
36304     validClass : 'has-success',
36305     iconTooltip : 'This field is required',
36306     indicatorpos : 'left',
36307     
36308     getAutoCreate : function(){
36309         
36310         var cls = "";
36311         if (!this.allowBlank) {
36312             cls  = "visible";
36313         }
36314         
36315         var cfg = {
36316             tag : this.tag,
36317             cls : 'roo-bootstrap-field-label ' + this.cls,
36318             for : this.target,
36319             cn : [
36320                 {
36321                     tag : 'i',
36322                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36323                     tooltip : this.iconTooltip
36324                 },
36325                 {
36326                     tag : 'span',
36327                     html : this.html
36328                 }
36329             ] 
36330         };
36331         
36332         if(this.indicatorpos == 'right'){
36333             var cfg = {
36334                 tag : this.tag,
36335                 cls : 'roo-bootstrap-field-label ' + this.cls,
36336                 for : this.target,
36337                 cn : [
36338                     {
36339                         tag : 'span',
36340                         html : this.html
36341                     },
36342                     {
36343                         tag : 'i',
36344                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36345                         tooltip : this.iconTooltip
36346                     }
36347                 ] 
36348             };
36349         }
36350         
36351         return cfg;
36352     },
36353     
36354     initEvents: function() 
36355     {
36356         Roo.bootstrap.Element.superclass.initEvents.call(this);
36357         
36358         this.indicator = this.indicatorEl();
36359         
36360         if(this.indicator){
36361             this.indicator.removeClass('visible');
36362             this.indicator.addClass('invisible');
36363         }
36364         
36365         Roo.bootstrap.form.FieldLabel.register(this);
36366     },
36367     
36368     indicatorEl : function()
36369     {
36370         var indicator = this.el.select('i.roo-required-indicator',true).first();
36371         
36372         if(!indicator){
36373             return false;
36374         }
36375         
36376         return indicator;
36377         
36378     },
36379     
36380     /**
36381      * Mark this field as valid
36382      */
36383     markValid : function()
36384     {
36385         if(this.indicator){
36386             this.indicator.removeClass('visible');
36387             this.indicator.addClass('invisible');
36388         }
36389         if (Roo.bootstrap.version == 3) {
36390             this.el.removeClass(this.invalidClass);
36391             this.el.addClass(this.validClass);
36392         } else {
36393             this.el.removeClass('is-invalid');
36394             this.el.addClass('is-valid');
36395         }
36396         
36397         
36398         this.fireEvent('valid', this);
36399     },
36400     
36401     /**
36402      * Mark this field as invalid
36403      * @param {String} msg The validation message
36404      */
36405     markInvalid : function(msg)
36406     {
36407         if(this.indicator){
36408             this.indicator.removeClass('invisible');
36409             this.indicator.addClass('visible');
36410         }
36411           if (Roo.bootstrap.version == 3) {
36412             this.el.removeClass(this.validClass);
36413             this.el.addClass(this.invalidClass);
36414         } else {
36415             this.el.removeClass('is-valid');
36416             this.el.addClass('is-invalid');
36417         }
36418         
36419         
36420         this.fireEvent('invalid', this, msg);
36421     }
36422     
36423    
36424 });
36425
36426 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36427     
36428     groups: {},
36429     
36430      /**
36431     * register a FieldLabel Group
36432     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36433     */
36434     register : function(label)
36435     {
36436         if(this.groups.hasOwnProperty(label.target)){
36437             return;
36438         }
36439      
36440         this.groups[label.target] = label;
36441         
36442     },
36443     /**
36444     * fetch a FieldLabel Group based on the target
36445     * @param {string} target
36446     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36447     */
36448     get: function(target) {
36449         if (typeof(this.groups[target]) == 'undefined') {
36450             return false;
36451         }
36452         
36453         return this.groups[target] ;
36454     }
36455 });
36456
36457  
36458
36459  /*
36460  * - LGPL
36461  *
36462  * page DateSplitField.
36463  * 
36464  */
36465
36466
36467 /**
36468  * @class Roo.bootstrap.form.DateSplitField
36469  * @extends Roo.bootstrap.Component
36470  * Bootstrap DateSplitField class
36471  * @cfg {string} fieldLabel - the label associated
36472  * @cfg {Number} labelWidth set the width of label (0-12)
36473  * @cfg {String} labelAlign (top|left)
36474  * @cfg {Boolean} dayAllowBlank (true|false) default false
36475  * @cfg {Boolean} monthAllowBlank (true|false) default false
36476  * @cfg {Boolean} yearAllowBlank (true|false) default false
36477  * @cfg {string} dayPlaceholder 
36478  * @cfg {string} monthPlaceholder
36479  * @cfg {string} yearPlaceholder
36480  * @cfg {string} dayFormat default 'd'
36481  * @cfg {string} monthFormat default 'm'
36482  * @cfg {string} yearFormat default 'Y'
36483  * @cfg {Number} labellg set the width of label (1-12)
36484  * @cfg {Number} labelmd set the width of label (1-12)
36485  * @cfg {Number} labelsm set the width of label (1-12)
36486  * @cfg {Number} labelxs set the width of label (1-12)
36487
36488  *     
36489  * @constructor
36490  * Create a new DateSplitField
36491  * @param {Object} config The config object
36492  */
36493
36494 Roo.bootstrap.form.DateSplitField = function(config){
36495     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36496     
36497     this.addEvents({
36498         // raw events
36499          /**
36500          * @event years
36501          * getting the data of years
36502          * @param {Roo.bootstrap.form.DateSplitField} this
36503          * @param {Object} years
36504          */
36505         "years" : true,
36506         /**
36507          * @event days
36508          * getting the data of days
36509          * @param {Roo.bootstrap.form.DateSplitField} this
36510          * @param {Object} days
36511          */
36512         "days" : true,
36513         /**
36514          * @event invalid
36515          * Fires after the field has been marked as invalid.
36516          * @param {Roo.form.Field} this
36517          * @param {String} msg The validation message
36518          */
36519         invalid : true,
36520        /**
36521          * @event valid
36522          * Fires after the field has been validated with no errors.
36523          * @param {Roo.form.Field} this
36524          */
36525         valid : true
36526     });
36527 };
36528
36529 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36530     
36531     fieldLabel : '',
36532     labelAlign : 'top',
36533     labelWidth : 3,
36534     dayAllowBlank : false,
36535     monthAllowBlank : false,
36536     yearAllowBlank : false,
36537     dayPlaceholder : '',
36538     monthPlaceholder : '',
36539     yearPlaceholder : '',
36540     dayFormat : 'd',
36541     monthFormat : 'm',
36542     yearFormat : 'Y',
36543     isFormField : true,
36544     labellg : 0,
36545     labelmd : 0,
36546     labelsm : 0,
36547     labelxs : 0,
36548     
36549     getAutoCreate : function()
36550     {
36551         var cfg = {
36552             tag : 'div',
36553             cls : 'row roo-date-split-field-group',
36554             cn : [
36555                 {
36556                     tag : 'input',
36557                     type : 'hidden',
36558                     cls : 'form-hidden-field roo-date-split-field-group-value',
36559                     name : this.name
36560                 }
36561             ]
36562         };
36563         
36564         var labelCls = 'col-md-12';
36565         var contentCls = 'col-md-4';
36566         
36567         if(this.fieldLabel){
36568             
36569             var label = {
36570                 tag : 'div',
36571                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36572                 cn : [
36573                     {
36574                         tag : 'label',
36575                         html : this.fieldLabel
36576                     }
36577                 ]
36578             };
36579             
36580             if(this.labelAlign == 'left'){
36581             
36582                 if(this.labelWidth > 12){
36583                     label.style = "width: " + this.labelWidth + 'px';
36584                 }
36585
36586                 if(this.labelWidth < 13 && this.labelmd == 0){
36587                     this.labelmd = this.labelWidth;
36588                 }
36589
36590                 if(this.labellg > 0){
36591                     labelCls = ' col-lg-' + this.labellg;
36592                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36593                 }
36594
36595                 if(this.labelmd > 0){
36596                     labelCls = ' col-md-' + this.labelmd;
36597                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36598                 }
36599
36600                 if(this.labelsm > 0){
36601                     labelCls = ' col-sm-' + this.labelsm;
36602                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36603                 }
36604
36605                 if(this.labelxs > 0){
36606                     labelCls = ' col-xs-' + this.labelxs;
36607                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36608                 }
36609             }
36610             
36611             label.cls += ' ' + labelCls;
36612             
36613             cfg.cn.push(label);
36614         }
36615         
36616         Roo.each(['day', 'month', 'year'], function(t){
36617             cfg.cn.push({
36618                 tag : 'div',
36619                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36620             });
36621         }, this);
36622         
36623         return cfg;
36624     },
36625     
36626     inputEl: function ()
36627     {
36628         return this.el.select('.roo-date-split-field-group-value', true).first();
36629     },
36630     
36631     onRender : function(ct, position) 
36632     {
36633         var _this = this;
36634         
36635         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36636         
36637         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36638         
36639         this.dayField = new Roo.bootstrap.form.ComboBox({
36640             allowBlank : this.dayAllowBlank,
36641             alwaysQuery : true,
36642             displayField : 'value',
36643             editable : false,
36644             fieldLabel : '',
36645             forceSelection : true,
36646             mode : 'local',
36647             placeholder : this.dayPlaceholder,
36648             selectOnFocus : true,
36649             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36650             triggerAction : 'all',
36651             typeAhead : true,
36652             valueField : 'value',
36653             store : new Roo.data.SimpleStore({
36654                 data : (function() {    
36655                     var days = [];
36656                     _this.fireEvent('days', _this, days);
36657                     return days;
36658                 })(),
36659                 fields : [ 'value' ]
36660             }),
36661             listeners : {
36662                 select : function (_self, record, index)
36663                 {
36664                     _this.setValue(_this.getValue());
36665                 }
36666             }
36667         });
36668
36669         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36670         
36671         this.monthField = new Roo.bootstrap.form.MonthField({
36672             after : '<i class=\"fa fa-calendar\"></i>',
36673             allowBlank : this.monthAllowBlank,
36674             placeholder : this.monthPlaceholder,
36675             readOnly : true,
36676             listeners : {
36677                 render : function (_self)
36678                 {
36679                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36680                         e.preventDefault();
36681                         _self.focus();
36682                     });
36683                 },
36684                 select : function (_self, oldvalue, newvalue)
36685                 {
36686                     _this.setValue(_this.getValue());
36687                 }
36688             }
36689         });
36690         
36691         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36692         
36693         this.yearField = new Roo.bootstrap.form.ComboBox({
36694             allowBlank : this.yearAllowBlank,
36695             alwaysQuery : true,
36696             displayField : 'value',
36697             editable : false,
36698             fieldLabel : '',
36699             forceSelection : true,
36700             mode : 'local',
36701             placeholder : this.yearPlaceholder,
36702             selectOnFocus : true,
36703             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36704             triggerAction : 'all',
36705             typeAhead : true,
36706             valueField : 'value',
36707             store : new Roo.data.SimpleStore({
36708                 data : (function() {
36709                     var years = [];
36710                     _this.fireEvent('years', _this, years);
36711                     return years;
36712                 })(),
36713                 fields : [ 'value' ]
36714             }),
36715             listeners : {
36716                 select : function (_self, record, index)
36717                 {
36718                     _this.setValue(_this.getValue());
36719                 }
36720             }
36721         });
36722
36723         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36724     },
36725     
36726     setValue : function(v, format)
36727     {
36728         this.inputEl.dom.value = v;
36729         
36730         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36731         
36732         var d = Date.parseDate(v, f);
36733         
36734         if(!d){
36735             this.validate();
36736             return;
36737         }
36738         
36739         this.setDay(d.format(this.dayFormat));
36740         this.setMonth(d.format(this.monthFormat));
36741         this.setYear(d.format(this.yearFormat));
36742         
36743         this.validate();
36744         
36745         return;
36746     },
36747     
36748     setDay : function(v)
36749     {
36750         this.dayField.setValue(v);
36751         this.inputEl.dom.value = this.getValue();
36752         this.validate();
36753         return;
36754     },
36755     
36756     setMonth : function(v)
36757     {
36758         this.monthField.setValue(v, true);
36759         this.inputEl.dom.value = this.getValue();
36760         this.validate();
36761         return;
36762     },
36763     
36764     setYear : function(v)
36765     {
36766         this.yearField.setValue(v);
36767         this.inputEl.dom.value = this.getValue();
36768         this.validate();
36769         return;
36770     },
36771     
36772     getDay : function()
36773     {
36774         return this.dayField.getValue();
36775     },
36776     
36777     getMonth : function()
36778     {
36779         return this.monthField.getValue();
36780     },
36781     
36782     getYear : function()
36783     {
36784         return this.yearField.getValue();
36785     },
36786     
36787     getValue : function()
36788     {
36789         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36790         
36791         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36792         
36793         return date;
36794     },
36795     
36796     reset : function()
36797     {
36798         this.setDay('');
36799         this.setMonth('');
36800         this.setYear('');
36801         this.inputEl.dom.value = '';
36802         this.validate();
36803         return;
36804     },
36805     
36806     validate : function()
36807     {
36808         var d = this.dayField.validate();
36809         var m = this.monthField.validate();
36810         var y = this.yearField.validate();
36811         
36812         var valid = true;
36813         
36814         if(
36815                 (!this.dayAllowBlank && !d) ||
36816                 (!this.monthAllowBlank && !m) ||
36817                 (!this.yearAllowBlank && !y)
36818         ){
36819             valid = false;
36820         }
36821         
36822         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36823             return valid;
36824         }
36825         
36826         if(valid){
36827             this.markValid();
36828             return valid;
36829         }
36830         
36831         this.markInvalid();
36832         
36833         return valid;
36834     },
36835     
36836     markValid : function()
36837     {
36838         
36839         var label = this.el.select('label', true).first();
36840         var icon = this.el.select('i.fa-star', true).first();
36841
36842         if(label && icon){
36843             icon.remove();
36844         }
36845         
36846         this.fireEvent('valid', this);
36847     },
36848     
36849      /**
36850      * Mark this field as invalid
36851      * @param {String} msg The validation message
36852      */
36853     markInvalid : function(msg)
36854     {
36855         
36856         var label = this.el.select('label', true).first();
36857         var icon = this.el.select('i.fa-star', true).first();
36858
36859         if(label && !icon){
36860             this.el.select('.roo-date-split-field-label', true).createChild({
36861                 tag : 'i',
36862                 cls : 'text-danger fa fa-lg fa-star',
36863                 tooltip : 'This field is required',
36864                 style : 'margin-right:5px;'
36865             }, label, true);
36866         }
36867         
36868         this.fireEvent('invalid', this, msg);
36869     },
36870     
36871     clearInvalid : function()
36872     {
36873         var label = this.el.select('label', true).first();
36874         var icon = this.el.select('i.fa-star', true).first();
36875
36876         if(label && icon){
36877             icon.remove();
36878         }
36879         
36880         this.fireEvent('valid', this);
36881     },
36882     
36883     getName: function()
36884     {
36885         return this.name;
36886     }
36887     
36888 });
36889
36890  
36891
36892 /**
36893  * @class Roo.bootstrap.LayoutMasonry
36894  * @extends Roo.bootstrap.Component
36895  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36896  * Bootstrap Layout Masonry class
36897  *
36898  * This is based on 
36899  * http://masonry.desandro.com
36900  *
36901  * The idea is to render all the bricks based on vertical width...
36902  *
36903  * The original code extends 'outlayer' - we might need to use that....
36904
36905  * @constructor
36906  * Create a new Element
36907  * @param {Object} config The config object
36908  */
36909
36910 Roo.bootstrap.LayoutMasonry = function(config){
36911     
36912     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36913     
36914     this.bricks = [];
36915     
36916     Roo.bootstrap.LayoutMasonry.register(this);
36917     
36918     this.addEvents({
36919         // raw events
36920         /**
36921          * @event layout
36922          * Fire after layout the items
36923          * @param {Roo.bootstrap.LayoutMasonry} this
36924          * @param {Roo.EventObject} e
36925          */
36926         "layout" : true
36927     });
36928     
36929 };
36930
36931 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36932     
36933     /**
36934      * @cfg {Boolean} isLayoutInstant = no animation?
36935      */   
36936     isLayoutInstant : false, // needed?
36937    
36938     /**
36939      * @cfg {Number} boxWidth  width of the columns
36940      */   
36941     boxWidth : 450,
36942     
36943       /**
36944      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36945      */   
36946     boxHeight : 0,
36947     
36948     /**
36949      * @cfg {Number} padWidth padding below box..
36950      */   
36951     padWidth : 10, 
36952     
36953     /**
36954      * @cfg {Number} gutter gutter width..
36955      */   
36956     gutter : 10,
36957     
36958      /**
36959      * @cfg {Number} maxCols maximum number of columns
36960      */   
36961     
36962     maxCols: 0,
36963     
36964     /**
36965      * @cfg {Boolean} isAutoInitial defalut true
36966      */   
36967     isAutoInitial : true, 
36968     
36969     containerWidth: 0,
36970     
36971     /**
36972      * @cfg {Boolean} isHorizontal defalut false
36973      */   
36974     isHorizontal : false, 
36975
36976     currentSize : null,
36977     
36978     tag: 'div',
36979     
36980     cls: '',
36981     
36982     bricks: null, //CompositeElement
36983     
36984     cols : 1,
36985     
36986     _isLayoutInited : false,
36987     
36988 //    isAlternative : false, // only use for vertical layout...
36989     
36990     /**
36991      * @cfg {Number} alternativePadWidth padding below box..
36992      */   
36993     alternativePadWidth : 50,
36994     
36995     selectedBrick : [],
36996     
36997     getAutoCreate : function(){
36998         
36999         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
37000         
37001         var cfg = {
37002             tag: this.tag,
37003             cls: 'blog-masonary-wrapper ' + this.cls,
37004             cn : {
37005                 cls : 'mas-boxes masonary'
37006             }
37007         };
37008         
37009         return cfg;
37010     },
37011     
37012     getChildContainer: function( )
37013     {
37014         if (this.boxesEl) {
37015             return this.boxesEl;
37016         }
37017         
37018         this.boxesEl = this.el.select('.mas-boxes').first();
37019         
37020         return this.boxesEl;
37021     },
37022     
37023     
37024     initEvents : function()
37025     {
37026         var _this = this;
37027         
37028         if(this.isAutoInitial){
37029             Roo.log('hook children rendered');
37030             this.on('childrenrendered', function() {
37031                 Roo.log('children rendered');
37032                 _this.initial();
37033             } ,this);
37034         }
37035     },
37036     
37037     initial : function()
37038     {
37039         this.selectedBrick = [];
37040         
37041         this.currentSize = this.el.getBox(true);
37042         
37043         Roo.EventManager.onWindowResize(this.resize, this); 
37044
37045         if(!this.isAutoInitial){
37046             this.layout();
37047             return;
37048         }
37049         
37050         this.layout();
37051         
37052         return;
37053         //this.layout.defer(500,this);
37054         
37055     },
37056     
37057     resize : function()
37058     {
37059         var cs = this.el.getBox(true);
37060         
37061         if (
37062                 this.currentSize.width == cs.width && 
37063                 this.currentSize.x == cs.x && 
37064                 this.currentSize.height == cs.height && 
37065                 this.currentSize.y == cs.y 
37066         ) {
37067             Roo.log("no change in with or X or Y");
37068             return;
37069         }
37070         
37071         this.currentSize = cs;
37072         
37073         this.layout();
37074         
37075     },
37076     
37077     layout : function()
37078     {   
37079         this._resetLayout();
37080         
37081         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37082         
37083         this.layoutItems( isInstant );
37084       
37085         this._isLayoutInited = true;
37086         
37087         this.fireEvent('layout', this);
37088         
37089     },
37090     
37091     _resetLayout : function()
37092     {
37093         if(this.isHorizontal){
37094             this.horizontalMeasureColumns();
37095             return;
37096         }
37097         
37098         this.verticalMeasureColumns();
37099         
37100     },
37101     
37102     verticalMeasureColumns : function()
37103     {
37104         this.getContainerWidth();
37105         
37106 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37107 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37108 //            return;
37109 //        }
37110         
37111         var boxWidth = this.boxWidth + this.padWidth;
37112         
37113         if(this.containerWidth < this.boxWidth){
37114             boxWidth = this.containerWidth
37115         }
37116         
37117         var containerWidth = this.containerWidth;
37118         
37119         var cols = Math.floor(containerWidth / boxWidth);
37120         
37121         this.cols = Math.max( cols, 1 );
37122         
37123         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37124         
37125         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37126         
37127         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37128         
37129         this.colWidth = boxWidth + avail - this.padWidth;
37130         
37131         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37132         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37133     },
37134     
37135     horizontalMeasureColumns : function()
37136     {
37137         this.getContainerWidth();
37138         
37139         var boxWidth = this.boxWidth;
37140         
37141         if(this.containerWidth < boxWidth){
37142             boxWidth = this.containerWidth;
37143         }
37144         
37145         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37146         
37147         this.el.setHeight(boxWidth);
37148         
37149     },
37150     
37151     getContainerWidth : function()
37152     {
37153         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37154     },
37155     
37156     layoutItems : function( isInstant )
37157     {
37158         Roo.log(this.bricks);
37159         
37160         var items = Roo.apply([], this.bricks);
37161         
37162         if(this.isHorizontal){
37163             this._horizontalLayoutItems( items , isInstant );
37164             return;
37165         }
37166         
37167 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37168 //            this._verticalAlternativeLayoutItems( items , isInstant );
37169 //            return;
37170 //        }
37171         
37172         this._verticalLayoutItems( items , isInstant );
37173         
37174     },
37175     
37176     _verticalLayoutItems : function ( items , isInstant)
37177     {
37178         if ( !items || !items.length ) {
37179             return;
37180         }
37181         
37182         var standard = [
37183             ['xs', 'xs', 'xs', 'tall'],
37184             ['xs', 'xs', 'tall'],
37185             ['xs', 'xs', 'sm'],
37186             ['xs', 'xs', 'xs'],
37187             ['xs', 'tall'],
37188             ['xs', 'sm'],
37189             ['xs', 'xs'],
37190             ['xs'],
37191             
37192             ['sm', 'xs', 'xs'],
37193             ['sm', 'xs'],
37194             ['sm'],
37195             
37196             ['tall', 'xs', 'xs', 'xs'],
37197             ['tall', 'xs', 'xs'],
37198             ['tall', 'xs'],
37199             ['tall']
37200             
37201         ];
37202         
37203         var queue = [];
37204         
37205         var boxes = [];
37206         
37207         var box = [];
37208         
37209         Roo.each(items, function(item, k){
37210             
37211             switch (item.size) {
37212                 // these layouts take up a full box,
37213                 case 'md' :
37214                 case 'md-left' :
37215                 case 'md-right' :
37216                 case 'wide' :
37217                     
37218                     if(box.length){
37219                         boxes.push(box);
37220                         box = [];
37221                     }
37222                     
37223                     boxes.push([item]);
37224                     
37225                     break;
37226                     
37227                 case 'xs' :
37228                 case 'sm' :
37229                 case 'tall' :
37230                     
37231                     box.push(item);
37232                     
37233                     break;
37234                 default :
37235                     break;
37236                     
37237             }
37238             
37239         }, this);
37240         
37241         if(box.length){
37242             boxes.push(box);
37243             box = [];
37244         }
37245         
37246         var filterPattern = function(box, length)
37247         {
37248             if(!box.length){
37249                 return;
37250             }
37251             
37252             var match = false;
37253             
37254             var pattern = box.slice(0, length);
37255             
37256             var format = [];
37257             
37258             Roo.each(pattern, function(i){
37259                 format.push(i.size);
37260             }, this);
37261             
37262             Roo.each(standard, function(s){
37263                 
37264                 if(String(s) != String(format)){
37265                     return;
37266                 }
37267                 
37268                 match = true;
37269                 return false;
37270                 
37271             }, this);
37272             
37273             if(!match && length == 1){
37274                 return;
37275             }
37276             
37277             if(!match){
37278                 filterPattern(box, length - 1);
37279                 return;
37280             }
37281                 
37282             queue.push(pattern);
37283
37284             box = box.slice(length, box.length);
37285
37286             filterPattern(box, 4);
37287
37288             return;
37289             
37290         }
37291         
37292         Roo.each(boxes, function(box, k){
37293             
37294             if(!box.length){
37295                 return;
37296             }
37297             
37298             if(box.length == 1){
37299                 queue.push(box);
37300                 return;
37301             }
37302             
37303             filterPattern(box, 4);
37304             
37305         }, this);
37306         
37307         this._processVerticalLayoutQueue( queue, isInstant );
37308         
37309     },
37310     
37311 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37312 //    {
37313 //        if ( !items || !items.length ) {
37314 //            return;
37315 //        }
37316 //
37317 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37318 //        
37319 //    },
37320     
37321     _horizontalLayoutItems : function ( items , isInstant)
37322     {
37323         if ( !items || !items.length || items.length < 3) {
37324             return;
37325         }
37326         
37327         items.reverse();
37328         
37329         var eItems = items.slice(0, 3);
37330         
37331         items = items.slice(3, items.length);
37332         
37333         var standard = [
37334             ['xs', 'xs', 'xs', 'wide'],
37335             ['xs', 'xs', 'wide'],
37336             ['xs', 'xs', 'sm'],
37337             ['xs', 'xs', 'xs'],
37338             ['xs', 'wide'],
37339             ['xs', 'sm'],
37340             ['xs', 'xs'],
37341             ['xs'],
37342             
37343             ['sm', 'xs', 'xs'],
37344             ['sm', 'xs'],
37345             ['sm'],
37346             
37347             ['wide', 'xs', 'xs', 'xs'],
37348             ['wide', 'xs', 'xs'],
37349             ['wide', 'xs'],
37350             ['wide'],
37351             
37352             ['wide-thin']
37353         ];
37354         
37355         var queue = [];
37356         
37357         var boxes = [];
37358         
37359         var box = [];
37360         
37361         Roo.each(items, function(item, k){
37362             
37363             switch (item.size) {
37364                 case 'md' :
37365                 case 'md-left' :
37366                 case 'md-right' :
37367                 case 'tall' :
37368                     
37369                     if(box.length){
37370                         boxes.push(box);
37371                         box = [];
37372                     }
37373                     
37374                     boxes.push([item]);
37375                     
37376                     break;
37377                     
37378                 case 'xs' :
37379                 case 'sm' :
37380                 case 'wide' :
37381                 case 'wide-thin' :
37382                     
37383                     box.push(item);
37384                     
37385                     break;
37386                 default :
37387                     break;
37388                     
37389             }
37390             
37391         }, this);
37392         
37393         if(box.length){
37394             boxes.push(box);
37395             box = [];
37396         }
37397         
37398         var filterPattern = function(box, length)
37399         {
37400             if(!box.length){
37401                 return;
37402             }
37403             
37404             var match = false;
37405             
37406             var pattern = box.slice(0, length);
37407             
37408             var format = [];
37409             
37410             Roo.each(pattern, function(i){
37411                 format.push(i.size);
37412             }, this);
37413             
37414             Roo.each(standard, function(s){
37415                 
37416                 if(String(s) != String(format)){
37417                     return;
37418                 }
37419                 
37420                 match = true;
37421                 return false;
37422                 
37423             }, this);
37424             
37425             if(!match && length == 1){
37426                 return;
37427             }
37428             
37429             if(!match){
37430                 filterPattern(box, length - 1);
37431                 return;
37432             }
37433                 
37434             queue.push(pattern);
37435
37436             box = box.slice(length, box.length);
37437
37438             filterPattern(box, 4);
37439
37440             return;
37441             
37442         }
37443         
37444         Roo.each(boxes, function(box, k){
37445             
37446             if(!box.length){
37447                 return;
37448             }
37449             
37450             if(box.length == 1){
37451                 queue.push(box);
37452                 return;
37453             }
37454             
37455             filterPattern(box, 4);
37456             
37457         }, this);
37458         
37459         
37460         var prune = [];
37461         
37462         var pos = this.el.getBox(true);
37463         
37464         var minX = pos.x;
37465         
37466         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37467         
37468         var hit_end = false;
37469         
37470         Roo.each(queue, function(box){
37471             
37472             if(hit_end){
37473                 
37474                 Roo.each(box, function(b){
37475                 
37476                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37477                     b.el.hide();
37478
37479                 }, this);
37480
37481                 return;
37482             }
37483             
37484             var mx = 0;
37485             
37486             Roo.each(box, function(b){
37487                 
37488                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37489                 b.el.show();
37490
37491                 mx = Math.max(mx, b.x);
37492                 
37493             }, this);
37494             
37495             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37496             
37497             if(maxX < minX){
37498                 
37499                 Roo.each(box, function(b){
37500                 
37501                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37502                     b.el.hide();
37503                     
37504                 }, this);
37505                 
37506                 hit_end = true;
37507                 
37508                 return;
37509             }
37510             
37511             prune.push(box);
37512             
37513         }, this);
37514         
37515         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37516     },
37517     
37518     /** Sets position of item in DOM
37519     * @param {Element} item
37520     * @param {Number} x - horizontal position
37521     * @param {Number} y - vertical position
37522     * @param {Boolean} isInstant - disables transitions
37523     */
37524     _processVerticalLayoutQueue : function( queue, isInstant )
37525     {
37526         var pos = this.el.getBox(true);
37527         var x = pos.x;
37528         var y = pos.y;
37529         var maxY = [];
37530         
37531         for (var i = 0; i < this.cols; i++){
37532             maxY[i] = pos.y;
37533         }
37534         
37535         Roo.each(queue, function(box, k){
37536             
37537             var col = k % this.cols;
37538             
37539             Roo.each(box, function(b,kk){
37540                 
37541                 b.el.position('absolute');
37542                 
37543                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37544                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37545                 
37546                 if(b.size == 'md-left' || b.size == 'md-right'){
37547                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37548                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37549                 }
37550                 
37551                 b.el.setWidth(width);
37552                 b.el.setHeight(height);
37553                 // iframe?
37554                 b.el.select('iframe',true).setSize(width,height);
37555                 
37556             }, this);
37557             
37558             for (var i = 0; i < this.cols; i++){
37559                 
37560                 if(maxY[i] < maxY[col]){
37561                     col = i;
37562                     continue;
37563                 }
37564                 
37565                 col = Math.min(col, i);
37566                 
37567             }
37568             
37569             x = pos.x + col * (this.colWidth + this.padWidth);
37570             
37571             y = maxY[col];
37572             
37573             var positions = [];
37574             
37575             switch (box.length){
37576                 case 1 :
37577                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37578                     break;
37579                 case 2 :
37580                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37581                     break;
37582                 case 3 :
37583                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37584                     break;
37585                 case 4 :
37586                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37587                     break;
37588                 default :
37589                     break;
37590             }
37591             
37592             Roo.each(box, function(b,kk){
37593                 
37594                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37595                 
37596                 var sz = b.el.getSize();
37597                 
37598                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37599                 
37600             }, this);
37601             
37602         }, this);
37603         
37604         var mY = 0;
37605         
37606         for (var i = 0; i < this.cols; i++){
37607             mY = Math.max(mY, maxY[i]);
37608         }
37609         
37610         this.el.setHeight(mY - pos.y);
37611         
37612     },
37613     
37614 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37615 //    {
37616 //        var pos = this.el.getBox(true);
37617 //        var x = pos.x;
37618 //        var y = pos.y;
37619 //        var maxX = pos.right;
37620 //        
37621 //        var maxHeight = 0;
37622 //        
37623 //        Roo.each(items, function(item, k){
37624 //            
37625 //            var c = k % 2;
37626 //            
37627 //            item.el.position('absolute');
37628 //                
37629 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37630 //
37631 //            item.el.setWidth(width);
37632 //
37633 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37634 //
37635 //            item.el.setHeight(height);
37636 //            
37637 //            if(c == 0){
37638 //                item.el.setXY([x, y], isInstant ? false : true);
37639 //            } else {
37640 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37641 //            }
37642 //            
37643 //            y = y + height + this.alternativePadWidth;
37644 //            
37645 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37646 //            
37647 //        }, this);
37648 //        
37649 //        this.el.setHeight(maxHeight);
37650 //        
37651 //    },
37652     
37653     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37654     {
37655         var pos = this.el.getBox(true);
37656         
37657         var minX = pos.x;
37658         var minY = pos.y;
37659         
37660         var maxX = pos.right;
37661         
37662         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37663         
37664         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37665         
37666         Roo.each(queue, function(box, k){
37667             
37668             Roo.each(box, function(b, kk){
37669                 
37670                 b.el.position('absolute');
37671                 
37672                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37673                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37674                 
37675                 if(b.size == 'md-left' || b.size == 'md-right'){
37676                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37677                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37678                 }
37679                 
37680                 b.el.setWidth(width);
37681                 b.el.setHeight(height);
37682                 
37683             }, this);
37684             
37685             if(!box.length){
37686                 return;
37687             }
37688             
37689             var positions = [];
37690             
37691             switch (box.length){
37692                 case 1 :
37693                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37694                     break;
37695                 case 2 :
37696                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37697                     break;
37698                 case 3 :
37699                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37700                     break;
37701                 case 4 :
37702                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37703                     break;
37704                 default :
37705                     break;
37706             }
37707             
37708             Roo.each(box, function(b,kk){
37709                 
37710                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37711                 
37712                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37713                 
37714             }, this);
37715             
37716         }, this);
37717         
37718     },
37719     
37720     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37721     {
37722         Roo.each(eItems, function(b,k){
37723             
37724             b.size = (k == 0) ? 'sm' : 'xs';
37725             b.x = (k == 0) ? 2 : 1;
37726             b.y = (k == 0) ? 2 : 1;
37727             
37728             b.el.position('absolute');
37729             
37730             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37731                 
37732             b.el.setWidth(width);
37733             
37734             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37735             
37736             b.el.setHeight(height);
37737             
37738         }, this);
37739
37740         var positions = [];
37741         
37742         positions.push({
37743             x : maxX - this.unitWidth * 2 - this.gutter,
37744             y : minY
37745         });
37746         
37747         positions.push({
37748             x : maxX - this.unitWidth,
37749             y : minY + (this.unitWidth + this.gutter) * 2
37750         });
37751         
37752         positions.push({
37753             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37754             y : minY
37755         });
37756         
37757         Roo.each(eItems, function(b,k){
37758             
37759             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37760
37761         }, this);
37762         
37763     },
37764     
37765     getVerticalOneBoxColPositions : function(x, y, box)
37766     {
37767         var pos = [];
37768         
37769         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37770         
37771         if(box[0].size == 'md-left'){
37772             rand = 0;
37773         }
37774         
37775         if(box[0].size == 'md-right'){
37776             rand = 1;
37777         }
37778         
37779         pos.push({
37780             x : x + (this.unitWidth + this.gutter) * rand,
37781             y : y
37782         });
37783         
37784         return pos;
37785     },
37786     
37787     getVerticalTwoBoxColPositions : function(x, y, box)
37788     {
37789         var pos = [];
37790         
37791         if(box[0].size == 'xs'){
37792             
37793             pos.push({
37794                 x : x,
37795                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37796             });
37797
37798             pos.push({
37799                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37800                 y : y
37801             });
37802             
37803             return pos;
37804             
37805         }
37806         
37807         pos.push({
37808             x : x,
37809             y : y
37810         });
37811
37812         pos.push({
37813             x : x + (this.unitWidth + this.gutter) * 2,
37814             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37815         });
37816         
37817         return pos;
37818         
37819     },
37820     
37821     getVerticalThreeBoxColPositions : function(x, y, box)
37822     {
37823         var pos = [];
37824         
37825         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37826             
37827             pos.push({
37828                 x : x,
37829                 y : y
37830             });
37831
37832             pos.push({
37833                 x : x + (this.unitWidth + this.gutter) * 1,
37834                 y : y
37835             });
37836             
37837             pos.push({
37838                 x : x + (this.unitWidth + this.gutter) * 2,
37839                 y : y
37840             });
37841             
37842             return pos;
37843             
37844         }
37845         
37846         if(box[0].size == 'xs' && box[1].size == 'xs'){
37847             
37848             pos.push({
37849                 x : x,
37850                 y : y
37851             });
37852
37853             pos.push({
37854                 x : x,
37855                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37856             });
37857             
37858             pos.push({
37859                 x : x + (this.unitWidth + this.gutter) * 1,
37860                 y : y
37861             });
37862             
37863             return pos;
37864             
37865         }
37866         
37867         pos.push({
37868             x : x,
37869             y : y
37870         });
37871
37872         pos.push({
37873             x : x + (this.unitWidth + this.gutter) * 2,
37874             y : y
37875         });
37876
37877         pos.push({
37878             x : x + (this.unitWidth + this.gutter) * 2,
37879             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37880         });
37881             
37882         return pos;
37883         
37884     },
37885     
37886     getVerticalFourBoxColPositions : function(x, y, box)
37887     {
37888         var pos = [];
37889         
37890         if(box[0].size == 'xs'){
37891             
37892             pos.push({
37893                 x : x,
37894                 y : y
37895             });
37896
37897             pos.push({
37898                 x : x,
37899                 y : y + (this.unitHeight + this.gutter) * 1
37900             });
37901             
37902             pos.push({
37903                 x : x,
37904                 y : y + (this.unitHeight + this.gutter) * 2
37905             });
37906             
37907             pos.push({
37908                 x : x + (this.unitWidth + this.gutter) * 1,
37909                 y : y
37910             });
37911             
37912             return pos;
37913             
37914         }
37915         
37916         pos.push({
37917             x : x,
37918             y : y
37919         });
37920
37921         pos.push({
37922             x : x + (this.unitWidth + this.gutter) * 2,
37923             y : y
37924         });
37925
37926         pos.push({
37927             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37928             y : y + (this.unitHeight + this.gutter) * 1
37929         });
37930
37931         pos.push({
37932             x : x + (this.unitWidth + this.gutter) * 2,
37933             y : y + (this.unitWidth + this.gutter) * 2
37934         });
37935
37936         return pos;
37937         
37938     },
37939     
37940     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37941     {
37942         var pos = [];
37943         
37944         if(box[0].size == 'md-left'){
37945             pos.push({
37946                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37947                 y : minY
37948             });
37949             
37950             return pos;
37951         }
37952         
37953         if(box[0].size == 'md-right'){
37954             pos.push({
37955                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37956                 y : minY + (this.unitWidth + this.gutter) * 1
37957             });
37958             
37959             return pos;
37960         }
37961         
37962         var rand = Math.floor(Math.random() * (4 - box[0].y));
37963         
37964         pos.push({
37965             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37966             y : minY + (this.unitWidth + this.gutter) * rand
37967         });
37968         
37969         return pos;
37970         
37971     },
37972     
37973     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37974     {
37975         var pos = [];
37976         
37977         if(box[0].size == 'xs'){
37978             
37979             pos.push({
37980                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37981                 y : minY
37982             });
37983
37984             pos.push({
37985                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37986                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37987             });
37988             
37989             return pos;
37990             
37991         }
37992         
37993         pos.push({
37994             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37995             y : minY
37996         });
37997
37998         pos.push({
37999             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38000             y : minY + (this.unitWidth + this.gutter) * 2
38001         });
38002         
38003         return pos;
38004         
38005     },
38006     
38007     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38008     {
38009         var pos = [];
38010         
38011         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38012             
38013             pos.push({
38014                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38015                 y : minY
38016             });
38017
38018             pos.push({
38019                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38020                 y : minY + (this.unitWidth + this.gutter) * 1
38021             });
38022             
38023             pos.push({
38024                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38025                 y : minY + (this.unitWidth + this.gutter) * 2
38026             });
38027             
38028             return pos;
38029             
38030         }
38031         
38032         if(box[0].size == 'xs' && box[1].size == 'xs'){
38033             
38034             pos.push({
38035                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38036                 y : minY
38037             });
38038
38039             pos.push({
38040                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38041                 y : minY
38042             });
38043             
38044             pos.push({
38045                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38046                 y : minY + (this.unitWidth + this.gutter) * 1
38047             });
38048             
38049             return pos;
38050             
38051         }
38052         
38053         pos.push({
38054             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38055             y : minY
38056         });
38057
38058         pos.push({
38059             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38060             y : minY + (this.unitWidth + this.gutter) * 2
38061         });
38062
38063         pos.push({
38064             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38065             y : minY + (this.unitWidth + this.gutter) * 2
38066         });
38067             
38068         return pos;
38069         
38070     },
38071     
38072     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38073     {
38074         var pos = [];
38075         
38076         if(box[0].size == 'xs'){
38077             
38078             pos.push({
38079                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38080                 y : minY
38081             });
38082
38083             pos.push({
38084                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38085                 y : minY
38086             });
38087             
38088             pos.push({
38089                 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),
38090                 y : minY
38091             });
38092             
38093             pos.push({
38094                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38095                 y : minY + (this.unitWidth + this.gutter) * 1
38096             });
38097             
38098             return pos;
38099             
38100         }
38101         
38102         pos.push({
38103             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38104             y : minY
38105         });
38106         
38107         pos.push({
38108             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38109             y : minY + (this.unitWidth + this.gutter) * 2
38110         });
38111         
38112         pos.push({
38113             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38114             y : minY + (this.unitWidth + this.gutter) * 2
38115         });
38116         
38117         pos.push({
38118             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),
38119             y : minY + (this.unitWidth + this.gutter) * 2
38120         });
38121
38122         return pos;
38123         
38124     },
38125     
38126     /**
38127     * remove a Masonry Brick
38128     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38129     */
38130     removeBrick : function(brick_id)
38131     {
38132         if (!brick_id) {
38133             return;
38134         }
38135         
38136         for (var i = 0; i<this.bricks.length; i++) {
38137             if (this.bricks[i].id == brick_id) {
38138                 this.bricks.splice(i,1);
38139                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38140                 this.initial();
38141             }
38142         }
38143     },
38144     
38145     /**
38146     * adds a Masonry Brick
38147     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38148     */
38149     addBrick : function(cfg)
38150     {
38151         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38152         //this.register(cn);
38153         cn.parentId = this.id;
38154         cn.render(this.el);
38155         return cn;
38156     },
38157     
38158     /**
38159     * register a Masonry Brick
38160     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38161     */
38162     
38163     register : function(brick)
38164     {
38165         this.bricks.push(brick);
38166         brick.masonryId = this.id;
38167     },
38168     
38169     /**
38170     * clear all the Masonry Brick
38171     */
38172     clearAll : function()
38173     {
38174         this.bricks = [];
38175         //this.getChildContainer().dom.innerHTML = "";
38176         this.el.dom.innerHTML = '';
38177     },
38178     
38179     getSelected : function()
38180     {
38181         if (!this.selectedBrick) {
38182             return false;
38183         }
38184         
38185         return this.selectedBrick;
38186     }
38187 });
38188
38189 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38190     
38191     groups: {},
38192      /**
38193     * register a Masonry Layout
38194     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38195     */
38196     
38197     register : function(layout)
38198     {
38199         this.groups[layout.id] = layout;
38200     },
38201     /**
38202     * fetch a  Masonry Layout based on the masonry layout ID
38203     * @param {string} the masonry layout to add
38204     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38205     */
38206     
38207     get: function(layout_id) {
38208         if (typeof(this.groups[layout_id]) == 'undefined') {
38209             return false;
38210         }
38211         return this.groups[layout_id] ;
38212     }
38213     
38214     
38215     
38216 });
38217
38218  
38219
38220  /**
38221  *
38222  * This is based on 
38223  * http://masonry.desandro.com
38224  *
38225  * The idea is to render all the bricks based on vertical width...
38226  *
38227  * The original code extends 'outlayer' - we might need to use that....
38228  * 
38229  */
38230
38231
38232 /**
38233  * @class Roo.bootstrap.LayoutMasonryAuto
38234  * @extends Roo.bootstrap.Component
38235  * Bootstrap Layout Masonry class
38236  * 
38237  * @constructor
38238  * Create a new Element
38239  * @param {Object} config The config object
38240  */
38241
38242 Roo.bootstrap.LayoutMasonryAuto = function(config){
38243     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38244 };
38245
38246 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38247     
38248       /**
38249      * @cfg {Boolean} isFitWidth  - resize the width..
38250      */   
38251     isFitWidth : false,  // options..
38252     /**
38253      * @cfg {Boolean} isOriginLeft = left align?
38254      */   
38255     isOriginLeft : true,
38256     /**
38257      * @cfg {Boolean} isOriginTop = top align?
38258      */   
38259     isOriginTop : false,
38260     /**
38261      * @cfg {Boolean} isLayoutInstant = no animation?
38262      */   
38263     isLayoutInstant : false, // needed?
38264     /**
38265      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38266      */   
38267     isResizingContainer : true,
38268     /**
38269      * @cfg {Number} columnWidth  width of the columns 
38270      */   
38271     
38272     columnWidth : 0,
38273     
38274     /**
38275      * @cfg {Number} maxCols maximum number of columns
38276      */   
38277     
38278     maxCols: 0,
38279     /**
38280      * @cfg {Number} padHeight padding below box..
38281      */   
38282     
38283     padHeight : 10, 
38284     
38285     /**
38286      * @cfg {Boolean} isAutoInitial defalut true
38287      */   
38288     
38289     isAutoInitial : true, 
38290     
38291     // private?
38292     gutter : 0,
38293     
38294     containerWidth: 0,
38295     initialColumnWidth : 0,
38296     currentSize : null,
38297     
38298     colYs : null, // array.
38299     maxY : 0,
38300     padWidth: 10,
38301     
38302     
38303     tag: 'div',
38304     cls: '',
38305     bricks: null, //CompositeElement
38306     cols : 0, // array?
38307     // element : null, // wrapped now this.el
38308     _isLayoutInited : null, 
38309     
38310     
38311     getAutoCreate : function(){
38312         
38313         var cfg = {
38314             tag: this.tag,
38315             cls: 'blog-masonary-wrapper ' + this.cls,
38316             cn : {
38317                 cls : 'mas-boxes masonary'
38318             }
38319         };
38320         
38321         return cfg;
38322     },
38323     
38324     getChildContainer: function( )
38325     {
38326         if (this.boxesEl) {
38327             return this.boxesEl;
38328         }
38329         
38330         this.boxesEl = this.el.select('.mas-boxes').first();
38331         
38332         return this.boxesEl;
38333     },
38334     
38335     
38336     initEvents : function()
38337     {
38338         var _this = this;
38339         
38340         if(this.isAutoInitial){
38341             Roo.log('hook children rendered');
38342             this.on('childrenrendered', function() {
38343                 Roo.log('children rendered');
38344                 _this.initial();
38345             } ,this);
38346         }
38347         
38348     },
38349     
38350     initial : function()
38351     {
38352         this.reloadItems();
38353
38354         this.currentSize = this.el.getBox(true);
38355
38356         /// was window resize... - let's see if this works..
38357         Roo.EventManager.onWindowResize(this.resize, this); 
38358
38359         if(!this.isAutoInitial){
38360             this.layout();
38361             return;
38362         }
38363         
38364         this.layout.defer(500,this);
38365     },
38366     
38367     reloadItems: function()
38368     {
38369         this.bricks = this.el.select('.masonry-brick', true);
38370         
38371         this.bricks.each(function(b) {
38372             //Roo.log(b.getSize());
38373             if (!b.attr('originalwidth')) {
38374                 b.attr('originalwidth',  b.getSize().width);
38375             }
38376             
38377         });
38378         
38379         Roo.log(this.bricks.elements.length);
38380     },
38381     
38382     resize : function()
38383     {
38384         Roo.log('resize');
38385         var cs = this.el.getBox(true);
38386         
38387         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38388             Roo.log("no change in with or X");
38389             return;
38390         }
38391         this.currentSize = cs;
38392         this.layout();
38393     },
38394     
38395     layout : function()
38396     {
38397          Roo.log('layout');
38398         this._resetLayout();
38399         //this._manageStamps();
38400       
38401         // don't animate first layout
38402         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38403         this.layoutItems( isInstant );
38404       
38405         // flag for initalized
38406         this._isLayoutInited = true;
38407     },
38408     
38409     layoutItems : function( isInstant )
38410     {
38411         //var items = this._getItemsForLayout( this.items );
38412         // original code supports filtering layout items.. we just ignore it..
38413         
38414         this._layoutItems( this.bricks , isInstant );
38415       
38416         this._postLayout();
38417     },
38418     _layoutItems : function ( items , isInstant)
38419     {
38420        //this.fireEvent( 'layout', this, items );
38421     
38422
38423         if ( !items || !items.elements.length ) {
38424           // no items, emit event with empty array
38425             return;
38426         }
38427
38428         var queue = [];
38429         items.each(function(item) {
38430             Roo.log("layout item");
38431             Roo.log(item);
38432             // get x/y object from method
38433             var position = this._getItemLayoutPosition( item );
38434             // enqueue
38435             position.item = item;
38436             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38437             queue.push( position );
38438         }, this);
38439       
38440         this._processLayoutQueue( queue );
38441     },
38442     /** Sets position of item in DOM
38443     * @param {Element} item
38444     * @param {Number} x - horizontal position
38445     * @param {Number} y - vertical position
38446     * @param {Boolean} isInstant - disables transitions
38447     */
38448     _processLayoutQueue : function( queue )
38449     {
38450         for ( var i=0, len = queue.length; i < len; i++ ) {
38451             var obj = queue[i];
38452             obj.item.position('absolute');
38453             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38454         }
38455     },
38456       
38457     
38458     /**
38459     * Any logic you want to do after each layout,
38460     * i.e. size the container
38461     */
38462     _postLayout : function()
38463     {
38464         this.resizeContainer();
38465     },
38466     
38467     resizeContainer : function()
38468     {
38469         if ( !this.isResizingContainer ) {
38470             return;
38471         }
38472         var size = this._getContainerSize();
38473         if ( size ) {
38474             this.el.setSize(size.width,size.height);
38475             this.boxesEl.setSize(size.width,size.height);
38476         }
38477     },
38478     
38479     
38480     
38481     _resetLayout : function()
38482     {
38483         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38484         this.colWidth = this.el.getWidth();
38485         //this.gutter = this.el.getWidth(); 
38486         
38487         this.measureColumns();
38488
38489         // reset column Y
38490         var i = this.cols;
38491         this.colYs = [];
38492         while (i--) {
38493             this.colYs.push( 0 );
38494         }
38495     
38496         this.maxY = 0;
38497     },
38498
38499     measureColumns : function()
38500     {
38501         this.getContainerWidth();
38502       // if columnWidth is 0, default to outerWidth of first item
38503         if ( !this.columnWidth ) {
38504             var firstItem = this.bricks.first();
38505             Roo.log(firstItem);
38506             this.columnWidth  = this.containerWidth;
38507             if (firstItem && firstItem.attr('originalwidth') ) {
38508                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38509             }
38510             // columnWidth fall back to item of first element
38511             Roo.log("set column width?");
38512                         this.initialColumnWidth = this.columnWidth  ;
38513
38514             // if first elem has no width, default to size of container
38515             
38516         }
38517         
38518         
38519         if (this.initialColumnWidth) {
38520             this.columnWidth = this.initialColumnWidth;
38521         }
38522         
38523         
38524             
38525         // column width is fixed at the top - however if container width get's smaller we should
38526         // reduce it...
38527         
38528         // this bit calcs how man columns..
38529             
38530         var columnWidth = this.columnWidth += this.gutter;
38531       
38532         // calculate columns
38533         var containerWidth = this.containerWidth + this.gutter;
38534         
38535         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38536         // fix rounding errors, typically with gutters
38537         var excess = columnWidth - containerWidth % columnWidth;
38538         
38539         
38540         // if overshoot is less than a pixel, round up, otherwise floor it
38541         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38542         cols = Math[ mathMethod ]( cols );
38543         this.cols = Math.max( cols, 1 );
38544         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38545         
38546          // padding positioning..
38547         var totalColWidth = this.cols * this.columnWidth;
38548         var padavail = this.containerWidth - totalColWidth;
38549         // so for 2 columns - we need 3 'pads'
38550         
38551         var padNeeded = (1+this.cols) * this.padWidth;
38552         
38553         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38554         
38555         this.columnWidth += padExtra
38556         //this.padWidth = Math.floor(padavail /  ( this.cols));
38557         
38558         // adjust colum width so that padding is fixed??
38559         
38560         // we have 3 columns ... total = width * 3
38561         // we have X left over... that should be used by 
38562         
38563         //if (this.expandC) {
38564             
38565         //}
38566         
38567         
38568         
38569     },
38570     
38571     getContainerWidth : function()
38572     {
38573        /* // container is parent if fit width
38574         var container = this.isFitWidth ? this.element.parentNode : this.element;
38575         // check that this.size and size are there
38576         // IE8 triggers resize on body size change, so they might not be
38577         
38578         var size = getSize( container );  //FIXME
38579         this.containerWidth = size && size.innerWidth; //FIXME
38580         */
38581          
38582         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38583         
38584     },
38585     
38586     _getItemLayoutPosition : function( item )  // what is item?
38587     {
38588         // we resize the item to our columnWidth..
38589       
38590         item.setWidth(this.columnWidth);
38591         item.autoBoxAdjust  = false;
38592         
38593         var sz = item.getSize();
38594  
38595         // how many columns does this brick span
38596         var remainder = this.containerWidth % this.columnWidth;
38597         
38598         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38599         // round if off by 1 pixel, otherwise use ceil
38600         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38601         colSpan = Math.min( colSpan, this.cols );
38602         
38603         // normally this should be '1' as we dont' currently allow multi width columns..
38604         
38605         var colGroup = this._getColGroup( colSpan );
38606         // get the minimum Y value from the columns
38607         var minimumY = Math.min.apply( Math, colGroup );
38608         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38609         
38610         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38611          
38612         // position the brick
38613         var position = {
38614             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38615             y: this.currentSize.y + minimumY + this.padHeight
38616         };
38617         
38618         Roo.log(position);
38619         // apply setHeight to necessary columns
38620         var setHeight = minimumY + sz.height + this.padHeight;
38621         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38622         
38623         var setSpan = this.cols + 1 - colGroup.length;
38624         for ( var i = 0; i < setSpan; i++ ) {
38625           this.colYs[ shortColIndex + i ] = setHeight ;
38626         }
38627       
38628         return position;
38629     },
38630     
38631     /**
38632      * @param {Number} colSpan - number of columns the element spans
38633      * @returns {Array} colGroup
38634      */
38635     _getColGroup : function( colSpan )
38636     {
38637         if ( colSpan < 2 ) {
38638           // if brick spans only one column, use all the column Ys
38639           return this.colYs;
38640         }
38641       
38642         var colGroup = [];
38643         // how many different places could this brick fit horizontally
38644         var groupCount = this.cols + 1 - colSpan;
38645         // for each group potential horizontal position
38646         for ( var i = 0; i < groupCount; i++ ) {
38647           // make an array of colY values for that one group
38648           var groupColYs = this.colYs.slice( i, i + colSpan );
38649           // and get the max value of the array
38650           colGroup[i] = Math.max.apply( Math, groupColYs );
38651         }
38652         return colGroup;
38653     },
38654     /*
38655     _manageStamp : function( stamp )
38656     {
38657         var stampSize =  stamp.getSize();
38658         var offset = stamp.getBox();
38659         // get the columns that this stamp affects
38660         var firstX = this.isOriginLeft ? offset.x : offset.right;
38661         var lastX = firstX + stampSize.width;
38662         var firstCol = Math.floor( firstX / this.columnWidth );
38663         firstCol = Math.max( 0, firstCol );
38664         
38665         var lastCol = Math.floor( lastX / this.columnWidth );
38666         // lastCol should not go over if multiple of columnWidth #425
38667         lastCol -= lastX % this.columnWidth ? 0 : 1;
38668         lastCol = Math.min( this.cols - 1, lastCol );
38669         
38670         // set colYs to bottom of the stamp
38671         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38672             stampSize.height;
38673             
38674         for ( var i = firstCol; i <= lastCol; i++ ) {
38675           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38676         }
38677     },
38678     */
38679     
38680     _getContainerSize : function()
38681     {
38682         this.maxY = Math.max.apply( Math, this.colYs );
38683         var size = {
38684             height: this.maxY
38685         };
38686       
38687         if ( this.isFitWidth ) {
38688             size.width = this._getContainerFitWidth();
38689         }
38690       
38691         return size;
38692     },
38693     
38694     _getContainerFitWidth : function()
38695     {
38696         var unusedCols = 0;
38697         // count unused columns
38698         var i = this.cols;
38699         while ( --i ) {
38700           if ( this.colYs[i] !== 0 ) {
38701             break;
38702           }
38703           unusedCols++;
38704         }
38705         // fit container to columns that have been used
38706         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38707     },
38708     
38709     needsResizeLayout : function()
38710     {
38711         var previousWidth = this.containerWidth;
38712         this.getContainerWidth();
38713         return previousWidth !== this.containerWidth;
38714     }
38715  
38716 });
38717
38718  
38719
38720  /*
38721  * - LGPL
38722  *
38723  * element
38724  * 
38725  */
38726
38727 /**
38728  * @class Roo.bootstrap.MasonryBrick
38729  * @extends Roo.bootstrap.Component
38730  * Bootstrap MasonryBrick class
38731  * 
38732  * @constructor
38733  * Create a new MasonryBrick
38734  * @param {Object} config The config object
38735  */
38736
38737 Roo.bootstrap.MasonryBrick = function(config){
38738     
38739     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38740     
38741     Roo.bootstrap.MasonryBrick.register(this);
38742     
38743     this.addEvents({
38744         // raw events
38745         /**
38746          * @event click
38747          * When a MasonryBrick is clcik
38748          * @param {Roo.bootstrap.MasonryBrick} this
38749          * @param {Roo.EventObject} e
38750          */
38751         "click" : true
38752     });
38753 };
38754
38755 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38756     
38757     /**
38758      * @cfg {String} title
38759      */   
38760     title : '',
38761     /**
38762      * @cfg {String} html
38763      */   
38764     html : '',
38765     /**
38766      * @cfg {String} bgimage
38767      */   
38768     bgimage : '',
38769     /**
38770      * @cfg {String} videourl
38771      */   
38772     videourl : '',
38773     /**
38774      * @cfg {String} cls
38775      */   
38776     cls : '',
38777     /**
38778      * @cfg {String} href
38779      */   
38780     href : '',
38781     /**
38782      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38783      */   
38784     size : 'xs',
38785     
38786     /**
38787      * @cfg {String} placetitle (center|bottom)
38788      */   
38789     placetitle : '',
38790     
38791     /**
38792      * @cfg {Boolean} isFitContainer defalut true
38793      */   
38794     isFitContainer : true, 
38795     
38796     /**
38797      * @cfg {Boolean} preventDefault defalut false
38798      */   
38799     preventDefault : false, 
38800     
38801     /**
38802      * @cfg {Boolean} inverse defalut false
38803      */   
38804     maskInverse : false, 
38805     
38806     getAutoCreate : function()
38807     {
38808         if(!this.isFitContainer){
38809             return this.getSplitAutoCreate();
38810         }
38811         
38812         var cls = 'masonry-brick masonry-brick-full';
38813         
38814         if(this.href.length){
38815             cls += ' masonry-brick-link';
38816         }
38817         
38818         if(this.bgimage.length){
38819             cls += ' masonry-brick-image';
38820         }
38821         
38822         if(this.maskInverse){
38823             cls += ' mask-inverse';
38824         }
38825         
38826         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38827             cls += ' enable-mask';
38828         }
38829         
38830         if(this.size){
38831             cls += ' masonry-' + this.size + '-brick';
38832         }
38833         
38834         if(this.placetitle.length){
38835             
38836             switch (this.placetitle) {
38837                 case 'center' :
38838                     cls += ' masonry-center-title';
38839                     break;
38840                 case 'bottom' :
38841                     cls += ' masonry-bottom-title';
38842                     break;
38843                 default:
38844                     break;
38845             }
38846             
38847         } else {
38848             if(!this.html.length && !this.bgimage.length){
38849                 cls += ' masonry-center-title';
38850             }
38851
38852             if(!this.html.length && this.bgimage.length){
38853                 cls += ' masonry-bottom-title';
38854             }
38855         }
38856         
38857         if(this.cls){
38858             cls += ' ' + this.cls;
38859         }
38860         
38861         var cfg = {
38862             tag: (this.href.length) ? 'a' : 'div',
38863             cls: cls,
38864             cn: [
38865                 {
38866                     tag: 'div',
38867                     cls: 'masonry-brick-mask'
38868                 },
38869                 {
38870                     tag: 'div',
38871                     cls: 'masonry-brick-paragraph',
38872                     cn: []
38873                 }
38874             ]
38875         };
38876         
38877         if(this.href.length){
38878             cfg.href = this.href;
38879         }
38880         
38881         var cn = cfg.cn[1].cn;
38882         
38883         if(this.title.length){
38884             cn.push({
38885                 tag: 'h4',
38886                 cls: 'masonry-brick-title',
38887                 html: this.title
38888             });
38889         }
38890         
38891         if(this.html.length){
38892             cn.push({
38893                 tag: 'p',
38894                 cls: 'masonry-brick-text',
38895                 html: this.html
38896             });
38897         }
38898         
38899         if (!this.title.length && !this.html.length) {
38900             cfg.cn[1].cls += ' hide';
38901         }
38902         
38903         if(this.bgimage.length){
38904             cfg.cn.push({
38905                 tag: 'img',
38906                 cls: 'masonry-brick-image-view',
38907                 src: this.bgimage
38908             });
38909         }
38910         
38911         if(this.videourl.length){
38912             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38913             // youtube support only?
38914             cfg.cn.push({
38915                 tag: 'iframe',
38916                 cls: 'masonry-brick-image-view',
38917                 src: vurl,
38918                 frameborder : 0,
38919                 allowfullscreen : true
38920             });
38921         }
38922         
38923         return cfg;
38924         
38925     },
38926     
38927     getSplitAutoCreate : function()
38928     {
38929         var cls = 'masonry-brick masonry-brick-split';
38930         
38931         if(this.href.length){
38932             cls += ' masonry-brick-link';
38933         }
38934         
38935         if(this.bgimage.length){
38936             cls += ' masonry-brick-image';
38937         }
38938         
38939         if(this.size){
38940             cls += ' masonry-' + this.size + '-brick';
38941         }
38942         
38943         switch (this.placetitle) {
38944             case 'center' :
38945                 cls += ' masonry-center-title';
38946                 break;
38947             case 'bottom' :
38948                 cls += ' masonry-bottom-title';
38949                 break;
38950             default:
38951                 if(!this.bgimage.length){
38952                     cls += ' masonry-center-title';
38953                 }
38954
38955                 if(this.bgimage.length){
38956                     cls += ' masonry-bottom-title';
38957                 }
38958                 break;
38959         }
38960         
38961         if(this.cls){
38962             cls += ' ' + this.cls;
38963         }
38964         
38965         var cfg = {
38966             tag: (this.href.length) ? 'a' : 'div',
38967             cls: cls,
38968             cn: [
38969                 {
38970                     tag: 'div',
38971                     cls: 'masonry-brick-split-head',
38972                     cn: [
38973                         {
38974                             tag: 'div',
38975                             cls: 'masonry-brick-paragraph',
38976                             cn: []
38977                         }
38978                     ]
38979                 },
38980                 {
38981                     tag: 'div',
38982                     cls: 'masonry-brick-split-body',
38983                     cn: []
38984                 }
38985             ]
38986         };
38987         
38988         if(this.href.length){
38989             cfg.href = this.href;
38990         }
38991         
38992         if(this.title.length){
38993             cfg.cn[0].cn[0].cn.push({
38994                 tag: 'h4',
38995                 cls: 'masonry-brick-title',
38996                 html: this.title
38997             });
38998         }
38999         
39000         if(this.html.length){
39001             cfg.cn[1].cn.push({
39002                 tag: 'p',
39003                 cls: 'masonry-brick-text',
39004                 html: this.html
39005             });
39006         }
39007
39008         if(this.bgimage.length){
39009             cfg.cn[0].cn.push({
39010                 tag: 'img',
39011                 cls: 'masonry-brick-image-view',
39012                 src: this.bgimage
39013             });
39014         }
39015         
39016         if(this.videourl.length){
39017             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39018             // youtube support only?
39019             cfg.cn[0].cn.cn.push({
39020                 tag: 'iframe',
39021                 cls: 'masonry-brick-image-view',
39022                 src: vurl,
39023                 frameborder : 0,
39024                 allowfullscreen : true
39025             });
39026         }
39027         
39028         return cfg;
39029     },
39030     
39031     initEvents: function() 
39032     {
39033         switch (this.size) {
39034             case 'xs' :
39035                 this.x = 1;
39036                 this.y = 1;
39037                 break;
39038             case 'sm' :
39039                 this.x = 2;
39040                 this.y = 2;
39041                 break;
39042             case 'md' :
39043             case 'md-left' :
39044             case 'md-right' :
39045                 this.x = 3;
39046                 this.y = 3;
39047                 break;
39048             case 'tall' :
39049                 this.x = 2;
39050                 this.y = 3;
39051                 break;
39052             case 'wide' :
39053                 this.x = 3;
39054                 this.y = 2;
39055                 break;
39056             case 'wide-thin' :
39057                 this.x = 3;
39058                 this.y = 1;
39059                 break;
39060                         
39061             default :
39062                 break;
39063         }
39064         
39065         if(Roo.isTouch){
39066             this.el.on('touchstart', this.onTouchStart, this);
39067             this.el.on('touchmove', this.onTouchMove, this);
39068             this.el.on('touchend', this.onTouchEnd, this);
39069             this.el.on('contextmenu', this.onContextMenu, this);
39070         } else {
39071             this.el.on('mouseenter'  ,this.enter, this);
39072             this.el.on('mouseleave', this.leave, this);
39073             this.el.on('click', this.onClick, this);
39074         }
39075         
39076         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39077             this.parent().bricks.push(this);   
39078         }
39079         
39080     },
39081     
39082     onClick: function(e, el)
39083     {
39084         var time = this.endTimer - this.startTimer;
39085         // Roo.log(e.preventDefault());
39086         if(Roo.isTouch){
39087             if(time > 1000){
39088                 e.preventDefault();
39089                 return;
39090             }
39091         }
39092         
39093         if(!this.preventDefault){
39094             return;
39095         }
39096         
39097         e.preventDefault();
39098         
39099         if (this.activeClass != '') {
39100             this.selectBrick();
39101         }
39102         
39103         this.fireEvent('click', this, e);
39104     },
39105     
39106     enter: function(e, el)
39107     {
39108         e.preventDefault();
39109         
39110         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39111             return;
39112         }
39113         
39114         if(this.bgimage.length && this.html.length){
39115             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39116         }
39117     },
39118     
39119     leave: function(e, el)
39120     {
39121         e.preventDefault();
39122         
39123         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39124             return;
39125         }
39126         
39127         if(this.bgimage.length && this.html.length){
39128             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39129         }
39130     },
39131     
39132     onTouchStart: function(e, el)
39133     {
39134 //        e.preventDefault();
39135         
39136         this.touchmoved = false;
39137         
39138         if(!this.isFitContainer){
39139             return;
39140         }
39141         
39142         if(!this.bgimage.length || !this.html.length){
39143             return;
39144         }
39145         
39146         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39147         
39148         this.timer = new Date().getTime();
39149         
39150     },
39151     
39152     onTouchMove: function(e, el)
39153     {
39154         this.touchmoved = true;
39155     },
39156     
39157     onContextMenu : function(e,el)
39158     {
39159         e.preventDefault();
39160         e.stopPropagation();
39161         return false;
39162     },
39163     
39164     onTouchEnd: function(e, el)
39165     {
39166 //        e.preventDefault();
39167         
39168         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39169         
39170             this.leave(e,el);
39171             
39172             return;
39173         }
39174         
39175         if(!this.bgimage.length || !this.html.length){
39176             
39177             if(this.href.length){
39178                 window.location.href = this.href;
39179             }
39180             
39181             return;
39182         }
39183         
39184         if(!this.isFitContainer){
39185             return;
39186         }
39187         
39188         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39189         
39190         window.location.href = this.href;
39191     },
39192     
39193     //selection on single brick only
39194     selectBrick : function() {
39195         
39196         if (!this.parentId) {
39197             return;
39198         }
39199         
39200         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39201         var index = m.selectedBrick.indexOf(this.id);
39202         
39203         if ( index > -1) {
39204             m.selectedBrick.splice(index,1);
39205             this.el.removeClass(this.activeClass);
39206             return;
39207         }
39208         
39209         for(var i = 0; i < m.selectedBrick.length; i++) {
39210             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39211             b.el.removeClass(b.activeClass);
39212         }
39213         
39214         m.selectedBrick = [];
39215         
39216         m.selectedBrick.push(this.id);
39217         this.el.addClass(this.activeClass);
39218         return;
39219     },
39220     
39221     isSelected : function(){
39222         return this.el.hasClass(this.activeClass);
39223         
39224     }
39225 });
39226
39227 Roo.apply(Roo.bootstrap.MasonryBrick, {
39228     
39229     //groups: {},
39230     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39231      /**
39232     * register a Masonry Brick
39233     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39234     */
39235     
39236     register : function(brick)
39237     {
39238         //this.groups[brick.id] = brick;
39239         this.groups.add(brick.id, brick);
39240     },
39241     /**
39242     * fetch a  masonry brick based on the masonry brick ID
39243     * @param {string} the masonry brick to add
39244     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39245     */
39246     
39247     get: function(brick_id) 
39248     {
39249         // if (typeof(this.groups[brick_id]) == 'undefined') {
39250         //     return false;
39251         // }
39252         // return this.groups[brick_id] ;
39253         
39254         if(this.groups.key(brick_id)) {
39255             return this.groups.key(brick_id);
39256         }
39257         
39258         return false;
39259     }
39260     
39261     
39262     
39263 });
39264
39265  /*
39266  * - LGPL
39267  *
39268  * element
39269  * 
39270  */
39271
39272 /**
39273  * @class Roo.bootstrap.Brick
39274  * @extends Roo.bootstrap.Component
39275  * Bootstrap Brick class
39276  * 
39277  * @constructor
39278  * Create a new Brick
39279  * @param {Object} config The config object
39280  */
39281
39282 Roo.bootstrap.Brick = function(config){
39283     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39284     
39285     this.addEvents({
39286         // raw events
39287         /**
39288          * @event click
39289          * When a Brick is click
39290          * @param {Roo.bootstrap.Brick} this
39291          * @param {Roo.EventObject} e
39292          */
39293         "click" : true
39294     });
39295 };
39296
39297 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39298     
39299     /**
39300      * @cfg {String} title
39301      */   
39302     title : '',
39303     /**
39304      * @cfg {String} html
39305      */   
39306     html : '',
39307     /**
39308      * @cfg {String} bgimage
39309      */   
39310     bgimage : '',
39311     /**
39312      * @cfg {String} cls
39313      */   
39314     cls : '',
39315     /**
39316      * @cfg {String} href
39317      */   
39318     href : '',
39319     /**
39320      * @cfg {String} video
39321      */   
39322     video : '',
39323     /**
39324      * @cfg {Boolean} square
39325      */   
39326     square : true,
39327     
39328     getAutoCreate : function()
39329     {
39330         var cls = 'roo-brick';
39331         
39332         if(this.href.length){
39333             cls += ' roo-brick-link';
39334         }
39335         
39336         if(this.bgimage.length){
39337             cls += ' roo-brick-image';
39338         }
39339         
39340         if(!this.html.length && !this.bgimage.length){
39341             cls += ' roo-brick-center-title';
39342         }
39343         
39344         if(!this.html.length && this.bgimage.length){
39345             cls += ' roo-brick-bottom-title';
39346         }
39347         
39348         if(this.cls){
39349             cls += ' ' + this.cls;
39350         }
39351         
39352         var cfg = {
39353             tag: (this.href.length) ? 'a' : 'div',
39354             cls: cls,
39355             cn: [
39356                 {
39357                     tag: 'div',
39358                     cls: 'roo-brick-paragraph',
39359                     cn: []
39360                 }
39361             ]
39362         };
39363         
39364         if(this.href.length){
39365             cfg.href = this.href;
39366         }
39367         
39368         var cn = cfg.cn[0].cn;
39369         
39370         if(this.title.length){
39371             cn.push({
39372                 tag: 'h4',
39373                 cls: 'roo-brick-title',
39374                 html: this.title
39375             });
39376         }
39377         
39378         if(this.html.length){
39379             cn.push({
39380                 tag: 'p',
39381                 cls: 'roo-brick-text',
39382                 html: this.html
39383             });
39384         } else {
39385             cn.cls += ' hide';
39386         }
39387         
39388         if(this.bgimage.length){
39389             cfg.cn.push({
39390                 tag: 'img',
39391                 cls: 'roo-brick-image-view',
39392                 src: this.bgimage
39393             });
39394         }
39395         
39396         return cfg;
39397     },
39398     
39399     initEvents: function() 
39400     {
39401         if(this.title.length || this.html.length){
39402             this.el.on('mouseenter'  ,this.enter, this);
39403             this.el.on('mouseleave', this.leave, this);
39404         }
39405         
39406         Roo.EventManager.onWindowResize(this.resize, this); 
39407         
39408         if(this.bgimage.length){
39409             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39410             this.imageEl.on('load', this.onImageLoad, this);
39411             return;
39412         }
39413         
39414         this.resize();
39415     },
39416     
39417     onImageLoad : function()
39418     {
39419         this.resize();
39420     },
39421     
39422     resize : function()
39423     {
39424         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39425         
39426         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39427         
39428         if(this.bgimage.length){
39429             var image = this.el.select('.roo-brick-image-view', true).first();
39430             
39431             image.setWidth(paragraph.getWidth());
39432             
39433             if(this.square){
39434                 image.setHeight(paragraph.getWidth());
39435             }
39436             
39437             this.el.setHeight(image.getHeight());
39438             paragraph.setHeight(image.getHeight());
39439             
39440         }
39441         
39442     },
39443     
39444     enter: function(e, el)
39445     {
39446         e.preventDefault();
39447         
39448         if(this.bgimage.length){
39449             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39450             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39451         }
39452     },
39453     
39454     leave: function(e, el)
39455     {
39456         e.preventDefault();
39457         
39458         if(this.bgimage.length){
39459             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39460             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39461         }
39462     }
39463     
39464 });
39465
39466  
39467
39468  /*
39469  * - LGPL
39470  *
39471  * Number field 
39472  */
39473
39474 /**
39475  * @class Roo.bootstrap.form.NumberField
39476  * @extends Roo.bootstrap.form.Input
39477  * Bootstrap NumberField class
39478  * 
39479  * 
39480  * 
39481  * 
39482  * @constructor
39483  * Create a new NumberField
39484  * @param {Object} config The config object
39485  */
39486
39487 Roo.bootstrap.form.NumberField = function(config){
39488     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39489 };
39490
39491 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39492     
39493     /**
39494      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39495      */
39496     allowDecimals : true,
39497     /**
39498      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39499      */
39500     decimalSeparator : ".",
39501     /**
39502      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39503      */
39504     decimalPrecision : 2,
39505     /**
39506      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39507      */
39508     allowNegative : true,
39509     
39510     /**
39511      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39512      */
39513     allowZero: true,
39514     /**
39515      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39516      */
39517     minValue : Number.NEGATIVE_INFINITY,
39518     /**
39519      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39520      */
39521     maxValue : Number.MAX_VALUE,
39522     /**
39523      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39524      */
39525     minText : "The minimum value for this field is {0}",
39526     /**
39527      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39528      */
39529     maxText : "The maximum value for this field is {0}",
39530     /**
39531      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39532      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39533      */
39534     nanText : "{0} is not a valid number",
39535     /**
39536      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39537      */
39538     thousandsDelimiter : false,
39539     /**
39540      * @cfg {String} valueAlign alignment of value
39541      */
39542     valueAlign : "left",
39543
39544     getAutoCreate : function()
39545     {
39546         var hiddenInput = {
39547             tag: 'input',
39548             type: 'hidden',
39549             id: Roo.id(),
39550             cls: 'hidden-number-input'
39551         };
39552         
39553         if (this.name) {
39554             hiddenInput.name = this.name;
39555         }
39556         
39557         this.name = '';
39558         
39559         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39560         
39561         this.name = hiddenInput.name;
39562         
39563         if(cfg.cn.length > 0) {
39564             cfg.cn.push(hiddenInput);
39565         }
39566         
39567         return cfg;
39568     },
39569
39570     // private
39571     initEvents : function()
39572     {   
39573         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39574         
39575         var allowed = "0123456789";
39576         
39577         if(this.allowDecimals){
39578             allowed += this.decimalSeparator;
39579         }
39580         
39581         if(this.allowNegative){
39582             allowed += "-";
39583         }
39584         
39585         if(this.thousandsDelimiter) {
39586             allowed += ",";
39587         }
39588         
39589         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39590         
39591         var keyPress = function(e){
39592             
39593             var k = e.getKey();
39594             
39595             var c = e.getCharCode();
39596             
39597             if(
39598                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39599                     allowed.indexOf(String.fromCharCode(c)) === -1
39600             ){
39601                 e.stopEvent();
39602                 return;
39603             }
39604             
39605             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39606                 return;
39607             }
39608             
39609             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39610                 e.stopEvent();
39611             }
39612         };
39613         
39614         this.el.on("keypress", keyPress, this);
39615     },
39616     
39617     validateValue : function(value)
39618     {
39619         
39620         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39621             return false;
39622         }
39623         
39624         var num = this.parseValue(value);
39625         
39626         if(isNaN(num)){
39627             this.markInvalid(String.format(this.nanText, value));
39628             return false;
39629         }
39630         
39631         if(num < this.minValue){
39632             this.markInvalid(String.format(this.minText, this.minValue));
39633             return false;
39634         }
39635         
39636         if(num > this.maxValue){
39637             this.markInvalid(String.format(this.maxText, this.maxValue));
39638             return false;
39639         }
39640         
39641         return true;
39642     },
39643
39644     getValue : function()
39645     {
39646         var v = this.hiddenEl().getValue();
39647         
39648         return this.fixPrecision(this.parseValue(v));
39649     },
39650
39651     parseValue : function(value)
39652     {
39653         if(this.thousandsDelimiter) {
39654             value += "";
39655             r = new RegExp(",", "g");
39656             value = value.replace(r, "");
39657         }
39658         
39659         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39660         return isNaN(value) ? '' : value;
39661     },
39662
39663     fixPrecision : function(value)
39664     {
39665         if(this.thousandsDelimiter) {
39666             value += "";
39667             r = new RegExp(",", "g");
39668             value = value.replace(r, "");
39669         }
39670         
39671         var nan = isNaN(value);
39672         
39673         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39674             return nan ? '' : value;
39675         }
39676         return parseFloat(value).toFixed(this.decimalPrecision);
39677     },
39678
39679     setValue : function(v)
39680     {
39681         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39682         
39683         this.value = v;
39684         
39685         if(this.rendered){
39686             
39687             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39688             
39689             this.inputEl().dom.value = (v == '') ? '' :
39690                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39691             
39692             if(!this.allowZero && v === '0') {
39693                 this.hiddenEl().dom.value = '';
39694                 this.inputEl().dom.value = '';
39695             }
39696             
39697             this.validate();
39698         }
39699     },
39700
39701     decimalPrecisionFcn : function(v)
39702     {
39703         return Math.floor(v);
39704     },
39705
39706     beforeBlur : function()
39707     {
39708         var v = this.parseValue(this.getRawValue());
39709         
39710         if(v || v === 0 || v === ''){
39711             this.setValue(v);
39712         }
39713     },
39714     
39715     hiddenEl : function()
39716     {
39717         return this.el.select('input.hidden-number-input',true).first();
39718     }
39719     
39720 });
39721
39722  
39723
39724 /*
39725 * Licence: LGPL
39726 */
39727
39728 /**
39729  * @class Roo.bootstrap.DocumentSlider
39730  * @extends Roo.bootstrap.Component
39731  * Bootstrap DocumentSlider class
39732  * 
39733  * @constructor
39734  * Create a new DocumentViewer
39735  * @param {Object} config The config object
39736  */
39737
39738 Roo.bootstrap.DocumentSlider = function(config){
39739     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39740     
39741     this.files = [];
39742     
39743     this.addEvents({
39744         /**
39745          * @event initial
39746          * Fire after initEvent
39747          * @param {Roo.bootstrap.DocumentSlider} this
39748          */
39749         "initial" : true,
39750         /**
39751          * @event update
39752          * Fire after update
39753          * @param {Roo.bootstrap.DocumentSlider} this
39754          */
39755         "update" : true,
39756         /**
39757          * @event click
39758          * Fire after click
39759          * @param {Roo.bootstrap.DocumentSlider} this
39760          */
39761         "click" : true
39762     });
39763 };
39764
39765 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39766     
39767     files : false,
39768     
39769     indicator : 0,
39770     
39771     getAutoCreate : function()
39772     {
39773         var cfg = {
39774             tag : 'div',
39775             cls : 'roo-document-slider',
39776             cn : [
39777                 {
39778                     tag : 'div',
39779                     cls : 'roo-document-slider-header',
39780                     cn : [
39781                         {
39782                             tag : 'div',
39783                             cls : 'roo-document-slider-header-title'
39784                         }
39785                     ]
39786                 },
39787                 {
39788                     tag : 'div',
39789                     cls : 'roo-document-slider-body',
39790                     cn : [
39791                         {
39792                             tag : 'div',
39793                             cls : 'roo-document-slider-prev',
39794                             cn : [
39795                                 {
39796                                     tag : 'i',
39797                                     cls : 'fa fa-chevron-left'
39798                                 }
39799                             ]
39800                         },
39801                         {
39802                             tag : 'div',
39803                             cls : 'roo-document-slider-thumb',
39804                             cn : [
39805                                 {
39806                                     tag : 'img',
39807                                     cls : 'roo-document-slider-image'
39808                                 }
39809                             ]
39810                         },
39811                         {
39812                             tag : 'div',
39813                             cls : 'roo-document-slider-next',
39814                             cn : [
39815                                 {
39816                                     tag : 'i',
39817                                     cls : 'fa fa-chevron-right'
39818                                 }
39819                             ]
39820                         }
39821                     ]
39822                 }
39823             ]
39824         };
39825         
39826         return cfg;
39827     },
39828     
39829     initEvents : function()
39830     {
39831         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39832         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39833         
39834         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39835         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39836         
39837         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39838         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39839         
39840         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39841         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39842         
39843         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39844         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39845         
39846         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39847         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39848         
39849         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39850         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39851         
39852         this.thumbEl.on('click', this.onClick, this);
39853         
39854         this.prevIndicator.on('click', this.prev, this);
39855         
39856         this.nextIndicator.on('click', this.next, this);
39857         
39858     },
39859     
39860     initial : function()
39861     {
39862         if(this.files.length){
39863             this.indicator = 1;
39864             this.update()
39865         }
39866         
39867         this.fireEvent('initial', this);
39868     },
39869     
39870     update : function()
39871     {
39872         this.imageEl.attr('src', this.files[this.indicator - 1]);
39873         
39874         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39875         
39876         this.prevIndicator.show();
39877         
39878         if(this.indicator == 1){
39879             this.prevIndicator.hide();
39880         }
39881         
39882         this.nextIndicator.show();
39883         
39884         if(this.indicator == this.files.length){
39885             this.nextIndicator.hide();
39886         }
39887         
39888         this.thumbEl.scrollTo('top');
39889         
39890         this.fireEvent('update', this);
39891     },
39892     
39893     onClick : function(e)
39894     {
39895         e.preventDefault();
39896         
39897         this.fireEvent('click', this);
39898     },
39899     
39900     prev : function(e)
39901     {
39902         e.preventDefault();
39903         
39904         this.indicator = Math.max(1, this.indicator - 1);
39905         
39906         this.update();
39907     },
39908     
39909     next : function(e)
39910     {
39911         e.preventDefault();
39912         
39913         this.indicator = Math.min(this.files.length, this.indicator + 1);
39914         
39915         this.update();
39916     }
39917 });
39918 /*
39919  * - LGPL
39920  *
39921  * RadioSet
39922  *
39923  *
39924  */
39925
39926 /**
39927  * @class Roo.bootstrap.form.RadioSet
39928  * @extends Roo.bootstrap.form.Input
39929  * @children Roo.bootstrap.form.Radio
39930  * Bootstrap RadioSet class
39931  * @cfg {String} indicatorpos (left|right) default left
39932  * @cfg {Boolean} inline (true|false) inline the element (default true)
39933  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39934  * @constructor
39935  * Create a new RadioSet
39936  * @param {Object} config The config object
39937  */
39938
39939 Roo.bootstrap.form.RadioSet = function(config){
39940     
39941     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39942     
39943     this.radioes = [];
39944     
39945     Roo.bootstrap.form.RadioSet.register(this);
39946     
39947     this.addEvents({
39948         /**
39949         * @event check
39950         * Fires when the element is checked or unchecked.
39951         * @param {Roo.bootstrap.form.RadioSet} this This radio
39952         * @param {Roo.bootstrap.form.Radio} item The checked item
39953         */
39954        check : true,
39955        /**
39956         * @event click
39957         * Fires when the element is click.
39958         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39959         * @param {Roo.bootstrap.form.Radio} item The checked item
39960         * @param {Roo.EventObject} e The event object
39961         */
39962        click : true
39963     });
39964     
39965 };
39966
39967 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39968
39969     radioes : false,
39970     
39971     inline : true,
39972     
39973     weight : '',
39974     
39975     indicatorpos : 'left',
39976     
39977     getAutoCreate : function()
39978     {
39979         var label = {
39980             tag : 'label',
39981             cls : 'roo-radio-set-label',
39982             cn : [
39983                 {
39984                     tag : 'span',
39985                     html : this.fieldLabel
39986                 }
39987             ]
39988         };
39989         if (Roo.bootstrap.version == 3) {
39990             
39991             
39992             if(this.indicatorpos == 'left'){
39993                 label.cn.unshift({
39994                     tag : 'i',
39995                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39996                     tooltip : 'This field is required'
39997                 });
39998             } else {
39999                 label.cn.push({
40000                     tag : 'i',
40001                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
40002                     tooltip : 'This field is required'
40003                 });
40004             }
40005         }
40006         var items = {
40007             tag : 'div',
40008             cls : 'roo-radio-set-items'
40009         };
40010         
40011         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40012         
40013         if (align === 'left' && this.fieldLabel.length) {
40014             
40015             items = {
40016                 cls : "roo-radio-set-right", 
40017                 cn: [
40018                     items
40019                 ]
40020             };
40021             
40022             if(this.labelWidth > 12){
40023                 label.style = "width: " + this.labelWidth + 'px';
40024             }
40025             
40026             if(this.labelWidth < 13 && this.labelmd == 0){
40027                 this.labelmd = this.labelWidth;
40028             }
40029             
40030             if(this.labellg > 0){
40031                 label.cls += ' col-lg-' + this.labellg;
40032                 items.cls += ' col-lg-' + (12 - this.labellg);
40033             }
40034             
40035             if(this.labelmd > 0){
40036                 label.cls += ' col-md-' + this.labelmd;
40037                 items.cls += ' col-md-' + (12 - this.labelmd);
40038             }
40039             
40040             if(this.labelsm > 0){
40041                 label.cls += ' col-sm-' + this.labelsm;
40042                 items.cls += ' col-sm-' + (12 - this.labelsm);
40043             }
40044             
40045             if(this.labelxs > 0){
40046                 label.cls += ' col-xs-' + this.labelxs;
40047                 items.cls += ' col-xs-' + (12 - this.labelxs);
40048             }
40049         }
40050         
40051         var cfg = {
40052             tag : 'div',
40053             cls : 'roo-radio-set',
40054             cn : [
40055                 {
40056                     tag : 'input',
40057                     cls : 'roo-radio-set-input',
40058                     type : 'hidden',
40059                     name : this.name,
40060                     value : this.value ? this.value :  ''
40061                 },
40062                 label,
40063                 items
40064             ]
40065         };
40066         
40067         if(this.weight.length){
40068             cfg.cls += ' roo-radio-' + this.weight;
40069         }
40070         
40071         if(this.inline) {
40072             cfg.cls += ' roo-radio-set-inline';
40073         }
40074         
40075         var settings=this;
40076         ['xs','sm','md','lg'].map(function(size){
40077             if (settings[size]) {
40078                 cfg.cls += ' col-' + size + '-' + settings[size];
40079             }
40080         });
40081         
40082         return cfg;
40083         
40084     },
40085
40086     initEvents : function()
40087     {
40088         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40089         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40090         
40091         if(!this.fieldLabel.length){
40092             this.labelEl.hide();
40093         }
40094         
40095         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40096         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40097         
40098         this.indicator = this.indicatorEl();
40099         
40100         if(this.indicator){
40101             this.indicator.addClass('invisible');
40102         }
40103         
40104         this.originalValue = this.getValue();
40105         
40106     },
40107     
40108     inputEl: function ()
40109     {
40110         return this.el.select('.roo-radio-set-input', true).first();
40111     },
40112     
40113     getChildContainer : function()
40114     {
40115         return this.itemsEl;
40116     },
40117     
40118     register : function(item)
40119     {
40120         this.radioes.push(item);
40121         
40122     },
40123     
40124     validate : function()
40125     {   
40126         if(this.getVisibilityEl().hasClass('hidden')){
40127             return true;
40128         }
40129         
40130         var valid = false;
40131         
40132         Roo.each(this.radioes, function(i){
40133             if(!i.checked){
40134                 return;
40135             }
40136             
40137             valid = true;
40138             return false;
40139         });
40140         
40141         if(this.allowBlank) {
40142             return true;
40143         }
40144         
40145         if(this.disabled || valid){
40146             this.markValid();
40147             return true;
40148         }
40149         
40150         this.markInvalid();
40151         return false;
40152         
40153     },
40154     
40155     markValid : function()
40156     {
40157         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40158             this.indicatorEl().removeClass('visible');
40159             this.indicatorEl().addClass('invisible');
40160         }
40161         
40162         
40163         if (Roo.bootstrap.version == 3) {
40164             this.el.removeClass([this.invalidClass, this.validClass]);
40165             this.el.addClass(this.validClass);
40166         } else {
40167             this.el.removeClass(['is-invalid','is-valid']);
40168             this.el.addClass(['is-valid']);
40169         }
40170         this.fireEvent('valid', this);
40171     },
40172     
40173     markInvalid : function(msg)
40174     {
40175         if(this.allowBlank || this.disabled){
40176             return;
40177         }
40178         
40179         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40180             this.indicatorEl().removeClass('invisible');
40181             this.indicatorEl().addClass('visible');
40182         }
40183         if (Roo.bootstrap.version == 3) {
40184             this.el.removeClass([this.invalidClass, this.validClass]);
40185             this.el.addClass(this.invalidClass);
40186         } else {
40187             this.el.removeClass(['is-invalid','is-valid']);
40188             this.el.addClass(['is-invalid']);
40189         }
40190         
40191         this.fireEvent('invalid', this, msg);
40192         
40193     },
40194     
40195     setValue : function(v, suppressEvent)
40196     {   
40197         if(this.value === v){
40198             return;
40199         }
40200         
40201         this.value = v;
40202         
40203         if(this.rendered){
40204             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40205         }
40206         
40207         Roo.each(this.radioes, function(i){
40208             i.checked = false;
40209             i.el.removeClass('checked');
40210         });
40211         
40212         Roo.each(this.radioes, function(i){
40213             
40214             if(i.value === v || i.value.toString() === v.toString()){
40215                 i.checked = true;
40216                 i.el.addClass('checked');
40217                 
40218                 if(suppressEvent !== true){
40219                     this.fireEvent('check', this, i);
40220                 }
40221                 
40222                 return false;
40223             }
40224             
40225         }, this);
40226         
40227         this.validate();
40228     },
40229     
40230     clearInvalid : function(){
40231         
40232         if(!this.el || this.preventMark){
40233             return;
40234         }
40235         
40236         this.el.removeClass([this.invalidClass]);
40237         
40238         this.fireEvent('valid', this);
40239     }
40240     
40241 });
40242
40243 Roo.apply(Roo.bootstrap.form.RadioSet, {
40244     
40245     groups: {},
40246     
40247     register : function(set)
40248     {
40249         this.groups[set.name] = set;
40250     },
40251     
40252     get: function(name) 
40253     {
40254         if (typeof(this.groups[name]) == 'undefined') {
40255             return false;
40256         }
40257         
40258         return this.groups[name] ;
40259     }
40260     
40261 });
40262 /*
40263  * Based on:
40264  * Ext JS Library 1.1.1
40265  * Copyright(c) 2006-2007, Ext JS, LLC.
40266  *
40267  * Originally Released Under LGPL - original licence link has changed is not relivant.
40268  *
40269  * Fork - LGPL
40270  * <script type="text/javascript">
40271  */
40272
40273
40274 /**
40275  * @class Roo.bootstrap.SplitBar
40276  * @extends Roo.util.Observable
40277  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40278  * <br><br>
40279  * Usage:
40280  * <pre><code>
40281 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40282                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40283 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40284 split.minSize = 100;
40285 split.maxSize = 600;
40286 split.animate = true;
40287 split.on('moved', splitterMoved);
40288 </code></pre>
40289  * @constructor
40290  * Create a new SplitBar
40291  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40292  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40293  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40294  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40295                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40296                         position of the SplitBar).
40297  */
40298 Roo.bootstrap.SplitBar = function(cfg){
40299     
40300     /** @private */
40301     
40302     //{
40303     //  dragElement : elm
40304     //  resizingElement: el,
40305         // optional..
40306     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40307     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40308         // existingProxy ???
40309     //}
40310     
40311     this.el = Roo.get(cfg.dragElement, true);
40312     this.el.dom.unselectable = "on";
40313     /** @private */
40314     this.resizingEl = Roo.get(cfg.resizingElement, true);
40315
40316     /**
40317      * @private
40318      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40319      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40320      * @type Number
40321      */
40322     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40323     
40324     /**
40325      * The minimum size of the resizing element. (Defaults to 0)
40326      * @type Number
40327      */
40328     this.minSize = 0;
40329     
40330     /**
40331      * The maximum size of the resizing element. (Defaults to 2000)
40332      * @type Number
40333      */
40334     this.maxSize = 2000;
40335     
40336     /**
40337      * Whether to animate the transition to the new size
40338      * @type Boolean
40339      */
40340     this.animate = false;
40341     
40342     /**
40343      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40344      * @type Boolean
40345      */
40346     this.useShim = false;
40347     
40348     /** @private */
40349     this.shim = null;
40350     
40351     if(!cfg.existingProxy){
40352         /** @private */
40353         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40354     }else{
40355         this.proxy = Roo.get(cfg.existingProxy).dom;
40356     }
40357     /** @private */
40358     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40359     
40360     /** @private */
40361     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40362     
40363     /** @private */
40364     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40365     
40366     /** @private */
40367     this.dragSpecs = {};
40368     
40369     /**
40370      * @private The adapter to use to positon and resize elements
40371      */
40372     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40373     this.adapter.init(this);
40374     
40375     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40376         /** @private */
40377         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40378         this.el.addClass("roo-splitbar-h");
40379     }else{
40380         /** @private */
40381         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40382         this.el.addClass("roo-splitbar-v");
40383     }
40384     
40385     this.addEvents({
40386         /**
40387          * @event resize
40388          * Fires when the splitter is moved (alias for {@link #event-moved})
40389          * @param {Roo.bootstrap.SplitBar} this
40390          * @param {Number} newSize the new width or height
40391          */
40392         "resize" : true,
40393         /**
40394          * @event moved
40395          * Fires when the splitter is moved
40396          * @param {Roo.bootstrap.SplitBar} this
40397          * @param {Number} newSize the new width or height
40398          */
40399         "moved" : true,
40400         /**
40401          * @event beforeresize
40402          * Fires before the splitter is dragged
40403          * @param {Roo.bootstrap.SplitBar} this
40404          */
40405         "beforeresize" : true,
40406
40407         "beforeapply" : true
40408     });
40409
40410     Roo.util.Observable.call(this);
40411 };
40412
40413 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40414     onStartProxyDrag : function(x, y){
40415         this.fireEvent("beforeresize", this);
40416         if(!this.overlay){
40417             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40418             o.unselectable();
40419             o.enableDisplayMode("block");
40420             // all splitbars share the same overlay
40421             Roo.bootstrap.SplitBar.prototype.overlay = o;
40422         }
40423         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40424         this.overlay.show();
40425         Roo.get(this.proxy).setDisplayed("block");
40426         var size = this.adapter.getElementSize(this);
40427         this.activeMinSize = this.getMinimumSize();;
40428         this.activeMaxSize = this.getMaximumSize();;
40429         var c1 = size - this.activeMinSize;
40430         var c2 = Math.max(this.activeMaxSize - size, 0);
40431         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40432             this.dd.resetConstraints();
40433             this.dd.setXConstraint(
40434                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40435                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40436             );
40437             this.dd.setYConstraint(0, 0);
40438         }else{
40439             this.dd.resetConstraints();
40440             this.dd.setXConstraint(0, 0);
40441             this.dd.setYConstraint(
40442                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40443                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40444             );
40445          }
40446         this.dragSpecs.startSize = size;
40447         this.dragSpecs.startPoint = [x, y];
40448         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40449     },
40450     
40451     /** 
40452      * @private Called after the drag operation by the DDProxy
40453      */
40454     onEndProxyDrag : function(e){
40455         Roo.get(this.proxy).setDisplayed(false);
40456         var endPoint = Roo.lib.Event.getXY(e);
40457         if(this.overlay){
40458             this.overlay.hide();
40459         }
40460         var newSize;
40461         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40462             newSize = this.dragSpecs.startSize + 
40463                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40464                     endPoint[0] - this.dragSpecs.startPoint[0] :
40465                     this.dragSpecs.startPoint[0] - endPoint[0]
40466                 );
40467         }else{
40468             newSize = this.dragSpecs.startSize + 
40469                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40470                     endPoint[1] - this.dragSpecs.startPoint[1] :
40471                     this.dragSpecs.startPoint[1] - endPoint[1]
40472                 );
40473         }
40474         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40475         if(newSize != this.dragSpecs.startSize){
40476             if(this.fireEvent('beforeapply', this, newSize) !== false){
40477                 this.adapter.setElementSize(this, newSize);
40478                 this.fireEvent("moved", this, newSize);
40479                 this.fireEvent("resize", this, newSize);
40480             }
40481         }
40482     },
40483     
40484     /**
40485      * Get the adapter this SplitBar uses
40486      * @return The adapter object
40487      */
40488     getAdapter : function(){
40489         return this.adapter;
40490     },
40491     
40492     /**
40493      * Set the adapter this SplitBar uses
40494      * @param {Object} adapter A SplitBar adapter object
40495      */
40496     setAdapter : function(adapter){
40497         this.adapter = adapter;
40498         this.adapter.init(this);
40499     },
40500     
40501     /**
40502      * Gets the minimum size for the resizing element
40503      * @return {Number} The minimum size
40504      */
40505     getMinimumSize : function(){
40506         return this.minSize;
40507     },
40508     
40509     /**
40510      * Sets the minimum size for the resizing element
40511      * @param {Number} minSize The minimum size
40512      */
40513     setMinimumSize : function(minSize){
40514         this.minSize = minSize;
40515     },
40516     
40517     /**
40518      * Gets the maximum size for the resizing element
40519      * @return {Number} The maximum size
40520      */
40521     getMaximumSize : function(){
40522         return this.maxSize;
40523     },
40524     
40525     /**
40526      * Sets the maximum size for the resizing element
40527      * @param {Number} maxSize The maximum size
40528      */
40529     setMaximumSize : function(maxSize){
40530         this.maxSize = maxSize;
40531     },
40532     
40533     /**
40534      * Sets the initialize size for the resizing element
40535      * @param {Number} size The initial size
40536      */
40537     setCurrentSize : function(size){
40538         var oldAnimate = this.animate;
40539         this.animate = false;
40540         this.adapter.setElementSize(this, size);
40541         this.animate = oldAnimate;
40542     },
40543     
40544     /**
40545      * Destroy this splitbar. 
40546      * @param {Boolean} removeEl True to remove the element
40547      */
40548     destroy : function(removeEl){
40549         if(this.shim){
40550             this.shim.remove();
40551         }
40552         this.dd.unreg();
40553         this.proxy.parentNode.removeChild(this.proxy);
40554         if(removeEl){
40555             this.el.remove();
40556         }
40557     }
40558 });
40559
40560 /**
40561  * @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.
40562  */
40563 Roo.bootstrap.SplitBar.createProxy = function(dir){
40564     var proxy = new Roo.Element(document.createElement("div"));
40565     proxy.unselectable();
40566     var cls = 'roo-splitbar-proxy';
40567     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40568     document.body.appendChild(proxy.dom);
40569     return proxy.dom;
40570 };
40571
40572 /** 
40573  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40574  * Default Adapter. It assumes the splitter and resizing element are not positioned
40575  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40576  */
40577 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40578 };
40579
40580 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40581     // do nothing for now
40582     init : function(s){
40583     
40584     },
40585     /**
40586      * Called before drag operations to get the current size of the resizing element. 
40587      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40588      */
40589      getElementSize : function(s){
40590         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40591             return s.resizingEl.getWidth();
40592         }else{
40593             return s.resizingEl.getHeight();
40594         }
40595     },
40596     
40597     /**
40598      * Called after drag operations to set the size of the resizing element.
40599      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40600      * @param {Number} newSize The new size to set
40601      * @param {Function} onComplete A function to be invoked when resizing is complete
40602      */
40603     setElementSize : function(s, newSize, onComplete){
40604         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40605             if(!s.animate){
40606                 s.resizingEl.setWidth(newSize);
40607                 if(onComplete){
40608                     onComplete(s, newSize);
40609                 }
40610             }else{
40611                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40612             }
40613         }else{
40614             
40615             if(!s.animate){
40616                 s.resizingEl.setHeight(newSize);
40617                 if(onComplete){
40618                     onComplete(s, newSize);
40619                 }
40620             }else{
40621                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40622             }
40623         }
40624     }
40625 };
40626
40627 /** 
40628  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40629  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40630  * Adapter that  moves the splitter element to align with the resized sizing element. 
40631  * Used with an absolute positioned SplitBar.
40632  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40633  * document.body, make sure you assign an id to the body element.
40634  */
40635 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40636     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40637     this.container = Roo.get(container);
40638 };
40639
40640 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40641     init : function(s){
40642         this.basic.init(s);
40643     },
40644     
40645     getElementSize : function(s){
40646         return this.basic.getElementSize(s);
40647     },
40648     
40649     setElementSize : function(s, newSize, onComplete){
40650         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40651     },
40652     
40653     moveSplitter : function(s){
40654         var yes = Roo.bootstrap.SplitBar;
40655         switch(s.placement){
40656             case yes.LEFT:
40657                 s.el.setX(s.resizingEl.getRight());
40658                 break;
40659             case yes.RIGHT:
40660                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40661                 break;
40662             case yes.TOP:
40663                 s.el.setY(s.resizingEl.getBottom());
40664                 break;
40665             case yes.BOTTOM:
40666                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40667                 break;
40668         }
40669     }
40670 };
40671
40672 /**
40673  * Orientation constant - Create a vertical SplitBar
40674  * @static
40675  * @type Number
40676  */
40677 Roo.bootstrap.SplitBar.VERTICAL = 1;
40678
40679 /**
40680  * Orientation constant - Create a horizontal SplitBar
40681  * @static
40682  * @type Number
40683  */
40684 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40685
40686 /**
40687  * Placement constant - The resizing element is to the left of the splitter element
40688  * @static
40689  * @type Number
40690  */
40691 Roo.bootstrap.SplitBar.LEFT = 1;
40692
40693 /**
40694  * Placement constant - The resizing element is to the right of the splitter element
40695  * @static
40696  * @type Number
40697  */
40698 Roo.bootstrap.SplitBar.RIGHT = 2;
40699
40700 /**
40701  * Placement constant - The resizing element is positioned above the splitter element
40702  * @static
40703  * @type Number
40704  */
40705 Roo.bootstrap.SplitBar.TOP = 3;
40706
40707 /**
40708  * Placement constant - The resizing element is positioned under splitter element
40709  * @static
40710  * @type Number
40711  */
40712 Roo.bootstrap.SplitBar.BOTTOM = 4;
40713 /*
40714  * Based on:
40715  * Ext JS Library 1.1.1
40716  * Copyright(c) 2006-2007, Ext JS, LLC.
40717  *
40718  * Originally Released Under LGPL - original licence link has changed is not relivant.
40719  *
40720  * Fork - LGPL
40721  * <script type="text/javascript">
40722  */
40723
40724 /**
40725  * @class Roo.bootstrap.layout.Manager
40726  * @extends Roo.bootstrap.Component
40727  * @abstract
40728  * Base class for layout managers.
40729  */
40730 Roo.bootstrap.layout.Manager = function(config)
40731 {
40732     this.monitorWindowResize = true; // do this before we apply configuration.
40733     
40734     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40735
40736
40737
40738
40739
40740     /** false to disable window resize monitoring @type Boolean */
40741     
40742     this.regions = {};
40743     this.addEvents({
40744         /**
40745          * @event layout
40746          * Fires when a layout is performed.
40747          * @param {Roo.LayoutManager} this
40748          */
40749         "layout" : true,
40750         /**
40751          * @event regionresized
40752          * Fires when the user resizes a region.
40753          * @param {Roo.LayoutRegion} region The resized region
40754          * @param {Number} newSize The new size (width for east/west, height for north/south)
40755          */
40756         "regionresized" : true,
40757         /**
40758          * @event regioncollapsed
40759          * Fires when a region is collapsed.
40760          * @param {Roo.LayoutRegion} region The collapsed region
40761          */
40762         "regioncollapsed" : true,
40763         /**
40764          * @event regionexpanded
40765          * Fires when a region is expanded.
40766          * @param {Roo.LayoutRegion} region The expanded region
40767          */
40768         "regionexpanded" : true
40769     });
40770     this.updating = false;
40771
40772     if (config.el) {
40773         this.el = Roo.get(config.el);
40774         this.initEvents();
40775     }
40776
40777 };
40778
40779 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40780
40781
40782     regions : null,
40783
40784     monitorWindowResize : true,
40785
40786
40787     updating : false,
40788
40789
40790     onRender : function(ct, position)
40791     {
40792         if(!this.el){
40793             this.el = Roo.get(ct);
40794             this.initEvents();
40795         }
40796         //this.fireEvent('render',this);
40797     },
40798
40799
40800     initEvents: function()
40801     {
40802
40803
40804         // ie scrollbar fix
40805         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40806             document.body.scroll = "no";
40807         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40808             this.el.position('relative');
40809         }
40810         this.id = this.el.id;
40811         this.el.addClass("roo-layout-container");
40812         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40813         if(this.el.dom != document.body ) {
40814             this.el.on('resize', this.layout,this);
40815             this.el.on('show', this.layout,this);
40816         }
40817
40818     },
40819
40820     /**
40821      * Returns true if this layout is currently being updated
40822      * @return {Boolean}
40823      */
40824     isUpdating : function(){
40825         return this.updating;
40826     },
40827
40828     /**
40829      * Suspend the LayoutManager from doing auto-layouts while
40830      * making multiple add or remove calls
40831      */
40832     beginUpdate : function(){
40833         this.updating = true;
40834     },
40835
40836     /**
40837      * Restore auto-layouts and optionally disable the manager from performing a layout
40838      * @param {Boolean} noLayout true to disable a layout update
40839      */
40840     endUpdate : function(noLayout){
40841         this.updating = false;
40842         if(!noLayout){
40843             this.layout();
40844         }
40845     },
40846
40847     layout: function(){
40848         // abstract...
40849     },
40850
40851     onRegionResized : function(region, newSize){
40852         this.fireEvent("regionresized", region, newSize);
40853         this.layout();
40854     },
40855
40856     onRegionCollapsed : function(region){
40857         this.fireEvent("regioncollapsed", region);
40858     },
40859
40860     onRegionExpanded : function(region){
40861         this.fireEvent("regionexpanded", region);
40862     },
40863
40864     /**
40865      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40866      * performs box-model adjustments.
40867      * @return {Object} The size as an object {width: (the width), height: (the height)}
40868      */
40869     getViewSize : function()
40870     {
40871         var size;
40872         if(this.el.dom != document.body){
40873             size = this.el.getSize();
40874         }else{
40875             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40876         }
40877         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40878         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40879         return size;
40880     },
40881
40882     /**
40883      * Returns the Element this layout is bound to.
40884      * @return {Roo.Element}
40885      */
40886     getEl : function(){
40887         return this.el;
40888     },
40889
40890     /**
40891      * Returns the specified region.
40892      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40893      * @return {Roo.LayoutRegion}
40894      */
40895     getRegion : function(target){
40896         return this.regions[target.toLowerCase()];
40897     },
40898
40899     onWindowResize : function(){
40900         if(this.monitorWindowResize){
40901             this.layout();
40902         }
40903     }
40904 });
40905 /*
40906  * Based on:
40907  * Ext JS Library 1.1.1
40908  * Copyright(c) 2006-2007, Ext JS, LLC.
40909  *
40910  * Originally Released Under LGPL - original licence link has changed is not relivant.
40911  *
40912  * Fork - LGPL
40913  * <script type="text/javascript">
40914  */
40915 /**
40916  * @class Roo.bootstrap.layout.Border
40917  * @extends Roo.bootstrap.layout.Manager
40918  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40919  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40920  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40921  * please see: examples/bootstrap/nested.html<br><br>
40922  
40923 <b>The container the layout is rendered into can be either the body element or any other element.
40924 If it is not the body element, the container needs to either be an absolute positioned element,
40925 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40926 the container size if it is not the body element.</b>
40927
40928 * @constructor
40929 * Create a new Border
40930 * @param {Object} config Configuration options
40931  */
40932 Roo.bootstrap.layout.Border = function(config){
40933     config = config || {};
40934     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40935     
40936     
40937     
40938     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40939         if(config[region]){
40940             config[region].region = region;
40941             this.addRegion(config[region]);
40942         }
40943     },this);
40944     
40945 };
40946
40947 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40948
40949 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40950     
40951         /**
40952          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40953          */
40954         /**
40955          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40956          */
40957         /**
40958          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40959          */
40960         /**
40961          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40962          */
40963         /**
40964          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40965          */
40966         
40967         
40968         
40969         
40970     parent : false, // this might point to a 'nest' or a ???
40971     
40972     /**
40973      * Creates and adds a new region if it doesn't already exist.
40974      * @param {String} target The target region key (north, south, east, west or center).
40975      * @param {Object} config The regions config object
40976      * @return {BorderLayoutRegion} The new region
40977      */
40978     addRegion : function(config)
40979     {
40980         if(!this.regions[config.region]){
40981             var r = this.factory(config);
40982             this.bindRegion(r);
40983         }
40984         return this.regions[config.region];
40985     },
40986
40987     // private (kinda)
40988     bindRegion : function(r){
40989         this.regions[r.config.region] = r;
40990         
40991         r.on("visibilitychange",    this.layout, this);
40992         r.on("paneladded",          this.layout, this);
40993         r.on("panelremoved",        this.layout, this);
40994         r.on("invalidated",         this.layout, this);
40995         r.on("resized",             this.onRegionResized, this);
40996         r.on("collapsed",           this.onRegionCollapsed, this);
40997         r.on("expanded",            this.onRegionExpanded, this);
40998     },
40999
41000     /**
41001      * Performs a layout update.
41002      */
41003     layout : function()
41004     {
41005         if(this.updating) {
41006             return;
41007         }
41008         
41009         // render all the rebions if they have not been done alreayd?
41010         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41011             if(this.regions[region] && !this.regions[region].bodyEl){
41012                 this.regions[region].onRender(this.el)
41013             }
41014         },this);
41015         
41016         var size = this.getViewSize();
41017         var w = size.width;
41018         var h = size.height;
41019         var centerW = w;
41020         var centerH = h;
41021         var centerY = 0;
41022         var centerX = 0;
41023         //var x = 0, y = 0;
41024
41025         var rs = this.regions;
41026         var north = rs["north"];
41027         var south = rs["south"]; 
41028         var west = rs["west"];
41029         var east = rs["east"];
41030         var center = rs["center"];
41031         //if(this.hideOnLayout){ // not supported anymore
41032             //c.el.setStyle("display", "none");
41033         //}
41034         if(north && north.isVisible()){
41035             var b = north.getBox();
41036             var m = north.getMargins();
41037             b.width = w - (m.left+m.right);
41038             b.x = m.left;
41039             b.y = m.top;
41040             centerY = b.height + b.y + m.bottom;
41041             centerH -= centerY;
41042             north.updateBox(this.safeBox(b));
41043         }
41044         if(south && south.isVisible()){
41045             var b = south.getBox();
41046             var m = south.getMargins();
41047             b.width = w - (m.left+m.right);
41048             b.x = m.left;
41049             var totalHeight = (b.height + m.top + m.bottom);
41050             b.y = h - totalHeight + m.top;
41051             centerH -= totalHeight;
41052             south.updateBox(this.safeBox(b));
41053         }
41054         if(west && west.isVisible()){
41055             var b = west.getBox();
41056             var m = west.getMargins();
41057             b.height = centerH - (m.top+m.bottom);
41058             b.x = m.left;
41059             b.y = centerY + m.top;
41060             var totalWidth = (b.width + m.left + m.right);
41061             centerX += totalWidth;
41062             centerW -= totalWidth;
41063             west.updateBox(this.safeBox(b));
41064         }
41065         if(east && east.isVisible()){
41066             var b = east.getBox();
41067             var m = east.getMargins();
41068             b.height = centerH - (m.top+m.bottom);
41069             var totalWidth = (b.width + m.left + m.right);
41070             b.x = w - totalWidth + m.left;
41071             b.y = centerY + m.top;
41072             centerW -= totalWidth;
41073             east.updateBox(this.safeBox(b));
41074         }
41075         if(center){
41076             var m = center.getMargins();
41077             var centerBox = {
41078                 x: centerX + m.left,
41079                 y: centerY + m.top,
41080                 width: centerW - (m.left+m.right),
41081                 height: centerH - (m.top+m.bottom)
41082             };
41083             //if(this.hideOnLayout){
41084                 //center.el.setStyle("display", "block");
41085             //}
41086             center.updateBox(this.safeBox(centerBox));
41087         }
41088         this.el.repaint();
41089         this.fireEvent("layout", this);
41090     },
41091
41092     // private
41093     safeBox : function(box){
41094         box.width = Math.max(0, box.width);
41095         box.height = Math.max(0, box.height);
41096         return box;
41097     },
41098
41099     /**
41100      * Adds a ContentPanel (or subclass) to this layout.
41101      * @param {String} target The target region key (north, south, east, west or center).
41102      * @param {Roo.ContentPanel} panel The panel to add
41103      * @return {Roo.ContentPanel} The added panel
41104      */
41105     add : function(target, panel){
41106          
41107         target = target.toLowerCase();
41108         return this.regions[target].add(panel);
41109     },
41110
41111     /**
41112      * Remove a ContentPanel (or subclass) to this layout.
41113      * @param {String} target The target region key (north, south, east, west or center).
41114      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41115      * @return {Roo.ContentPanel} The removed panel
41116      */
41117     remove : function(target, panel){
41118         target = target.toLowerCase();
41119         return this.regions[target].remove(panel);
41120     },
41121
41122     /**
41123      * Searches all regions for a panel with the specified id
41124      * @param {String} panelId
41125      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41126      */
41127     findPanel : function(panelId){
41128         var rs = this.regions;
41129         for(var target in rs){
41130             if(typeof rs[target] != "function"){
41131                 var p = rs[target].getPanel(panelId);
41132                 if(p){
41133                     return p;
41134                 }
41135             }
41136         }
41137         return null;
41138     },
41139
41140     /**
41141      * Searches all regions for a panel with the specified id and activates (shows) it.
41142      * @param {String/ContentPanel} panelId The panels id or the panel itself
41143      * @return {Roo.ContentPanel} The shown panel or null
41144      */
41145     showPanel : function(panelId) {
41146       var rs = this.regions;
41147       for(var target in rs){
41148          var r = rs[target];
41149          if(typeof r != "function"){
41150             if(r.hasPanel(panelId)){
41151                return r.showPanel(panelId);
41152             }
41153          }
41154       }
41155       return null;
41156    },
41157
41158    /**
41159      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41160      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41161      */
41162    /*
41163     restoreState : function(provider){
41164         if(!provider){
41165             provider = Roo.state.Manager;
41166         }
41167         var sm = new Roo.LayoutStateManager();
41168         sm.init(this, provider);
41169     },
41170 */
41171  
41172  
41173     /**
41174      * Adds a xtype elements to the layout.
41175      * <pre><code>
41176
41177 layout.addxtype({
41178        xtype : 'ContentPanel',
41179        region: 'west',
41180        items: [ .... ]
41181    }
41182 );
41183
41184 layout.addxtype({
41185         xtype : 'NestedLayoutPanel',
41186         region: 'west',
41187         layout: {
41188            center: { },
41189            west: { }   
41190         },
41191         items : [ ... list of content panels or nested layout panels.. ]
41192    }
41193 );
41194 </code></pre>
41195      * @param {Object} cfg Xtype definition of item to add.
41196      */
41197     addxtype : function(cfg)
41198     {
41199         // basically accepts a pannel...
41200         // can accept a layout region..!?!?
41201         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41202         
41203         
41204         // theory?  children can only be panels??
41205         
41206         //if (!cfg.xtype.match(/Panel$/)) {
41207         //    return false;
41208         //}
41209         var ret = false;
41210         
41211         if (typeof(cfg.region) == 'undefined') {
41212             Roo.log("Failed to add Panel, region was not set");
41213             Roo.log(cfg);
41214             return false;
41215         }
41216         var region = cfg.region;
41217         delete cfg.region;
41218         
41219           
41220         var xitems = [];
41221         if (cfg.items) {
41222             xitems = cfg.items;
41223             delete cfg.items;
41224         }
41225         var nb = false;
41226         
41227         if ( region == 'center') {
41228             Roo.log("Center: " + cfg.title);
41229         }
41230         
41231         
41232         switch(cfg.xtype) 
41233         {
41234             case 'Content':  // ContentPanel (el, cfg)
41235             case 'Scroll':  // ContentPanel (el, cfg)
41236             case 'View': 
41237                 cfg.autoCreate = cfg.autoCreate || true;
41238                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41239                 //} else {
41240                 //    var el = this.el.createChild();
41241                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41242                 //}
41243                 
41244                 this.add(region, ret);
41245                 break;
41246             
41247             /*
41248             case 'TreePanel': // our new panel!
41249                 cfg.el = this.el.createChild();
41250                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41251                 this.add(region, ret);
41252                 break;
41253             */
41254             
41255             case 'Nest': 
41256                 // create a new Layout (which is  a Border Layout...
41257                 
41258                 var clayout = cfg.layout;
41259                 clayout.el  = this.el.createChild();
41260                 clayout.items   = clayout.items  || [];
41261                 
41262                 delete cfg.layout;
41263                 
41264                 // replace this exitems with the clayout ones..
41265                 xitems = clayout.items;
41266                  
41267                 // force background off if it's in center...
41268                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41269                     cfg.background = false;
41270                 }
41271                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41272                 
41273                 
41274                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41275                 //console.log('adding nested layout panel '  + cfg.toSource());
41276                 this.add(region, ret);
41277                 nb = {}; /// find first...
41278                 break;
41279             
41280             case 'Grid':
41281                 
41282                 // needs grid and region
41283                 
41284                 //var el = this.getRegion(region).el.createChild();
41285                 /*
41286                  *var el = this.el.createChild();
41287                 // create the grid first...
41288                 cfg.grid.container = el;
41289                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41290                 */
41291                 
41292                 if (region == 'center' && this.active ) {
41293                     cfg.background = false;
41294                 }
41295                 
41296                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41297                 
41298                 this.add(region, ret);
41299                 /*
41300                 if (cfg.background) {
41301                     // render grid on panel activation (if panel background)
41302                     ret.on('activate', function(gp) {
41303                         if (!gp.grid.rendered) {
41304                     //        gp.grid.render(el);
41305                         }
41306                     });
41307                 } else {
41308                   //  cfg.grid.render(el);
41309                 }
41310                 */
41311                 break;
41312            
41313            
41314             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41315                 // it was the old xcomponent building that caused this before.
41316                 // espeically if border is the top element in the tree.
41317                 ret = this;
41318                 break; 
41319                 
41320                     
41321                 
41322                 
41323                 
41324             default:
41325                 /*
41326                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41327                     
41328                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41329                     this.add(region, ret);
41330                 } else {
41331                 */
41332                     Roo.log(cfg);
41333                     throw "Can not add '" + cfg.xtype + "' to Border";
41334                     return null;
41335              
41336                                 
41337              
41338         }
41339         this.beginUpdate();
41340         // add children..
41341         var region = '';
41342         var abn = {};
41343         Roo.each(xitems, function(i)  {
41344             region = nb && i.region ? i.region : false;
41345             
41346             var add = ret.addxtype(i);
41347            
41348             if (region) {
41349                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41350                 if (!i.background) {
41351                     abn[region] = nb[region] ;
41352                 }
41353             }
41354             
41355         });
41356         this.endUpdate();
41357
41358         // make the last non-background panel active..
41359         //if (nb) { Roo.log(abn); }
41360         if (nb) {
41361             
41362             for(var r in abn) {
41363                 region = this.getRegion(r);
41364                 if (region) {
41365                     // tried using nb[r], but it does not work..
41366                      
41367                     region.showPanel(abn[r]);
41368                    
41369                 }
41370             }
41371         }
41372         return ret;
41373         
41374     },
41375     
41376     
41377 // private
41378     factory : function(cfg)
41379     {
41380         
41381         var validRegions = Roo.bootstrap.layout.Border.regions;
41382
41383         var target = cfg.region;
41384         cfg.mgr = this;
41385         
41386         var r = Roo.bootstrap.layout;
41387         Roo.log(target);
41388         switch(target){
41389             case "north":
41390                 return new r.North(cfg);
41391             case "south":
41392                 return new r.South(cfg);
41393             case "east":
41394                 return new r.East(cfg);
41395             case "west":
41396                 return new r.West(cfg);
41397             case "center":
41398                 return new r.Center(cfg);
41399         }
41400         throw 'Layout region "'+target+'" not supported.';
41401     }
41402     
41403     
41404 });
41405  /*
41406  * Based on:
41407  * Ext JS Library 1.1.1
41408  * Copyright(c) 2006-2007, Ext JS, LLC.
41409  *
41410  * Originally Released Under LGPL - original licence link has changed is not relivant.
41411  *
41412  * Fork - LGPL
41413  * <script type="text/javascript">
41414  */
41415  
41416 /**
41417  * @class Roo.bootstrap.layout.Basic
41418  * @extends Roo.util.Observable
41419  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41420  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41421  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41422  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41423  * @cfg {string}   region  the region that it inhabits..
41424  * @cfg {bool}   skipConfig skip config?
41425  * 
41426
41427  */
41428 Roo.bootstrap.layout.Basic = function(config){
41429     
41430     this.mgr = config.mgr;
41431     
41432     this.position = config.region;
41433     
41434     var skipConfig = config.skipConfig;
41435     
41436     this.events = {
41437         /**
41438          * @scope Roo.BasicLayoutRegion
41439          */
41440         
41441         /**
41442          * @event beforeremove
41443          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41444          * @param {Roo.LayoutRegion} this
41445          * @param {Roo.ContentPanel} panel The panel
41446          * @param {Object} e The cancel event object
41447          */
41448         "beforeremove" : true,
41449         /**
41450          * @event invalidated
41451          * Fires when the layout for this region is changed.
41452          * @param {Roo.LayoutRegion} this
41453          */
41454         "invalidated" : true,
41455         /**
41456          * @event visibilitychange
41457          * Fires when this region is shown or hidden 
41458          * @param {Roo.LayoutRegion} this
41459          * @param {Boolean} visibility true or false
41460          */
41461         "visibilitychange" : true,
41462         /**
41463          * @event paneladded
41464          * Fires when a panel is added. 
41465          * @param {Roo.LayoutRegion} this
41466          * @param {Roo.ContentPanel} panel The panel
41467          */
41468         "paneladded" : true,
41469         /**
41470          * @event panelremoved
41471          * Fires when a panel is removed. 
41472          * @param {Roo.LayoutRegion} this
41473          * @param {Roo.ContentPanel} panel The panel
41474          */
41475         "panelremoved" : true,
41476         /**
41477          * @event beforecollapse
41478          * Fires when this region before collapse.
41479          * @param {Roo.LayoutRegion} this
41480          */
41481         "beforecollapse" : true,
41482         /**
41483          * @event collapsed
41484          * Fires when this region is collapsed.
41485          * @param {Roo.LayoutRegion} this
41486          */
41487         "collapsed" : true,
41488         /**
41489          * @event expanded
41490          * Fires when this region is expanded.
41491          * @param {Roo.LayoutRegion} this
41492          */
41493         "expanded" : true,
41494         /**
41495          * @event slideshow
41496          * Fires when this region is slid into view.
41497          * @param {Roo.LayoutRegion} this
41498          */
41499         "slideshow" : true,
41500         /**
41501          * @event slidehide
41502          * Fires when this region slides out of view. 
41503          * @param {Roo.LayoutRegion} this
41504          */
41505         "slidehide" : true,
41506         /**
41507          * @event panelactivated
41508          * Fires when a panel is activated. 
41509          * @param {Roo.LayoutRegion} this
41510          * @param {Roo.ContentPanel} panel The activated panel
41511          */
41512         "panelactivated" : true,
41513         /**
41514          * @event resized
41515          * Fires when the user resizes this region. 
41516          * @param {Roo.LayoutRegion} this
41517          * @param {Number} newSize The new size (width for east/west, height for north/south)
41518          */
41519         "resized" : true
41520     };
41521     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41522     this.panels = new Roo.util.MixedCollection();
41523     this.panels.getKey = this.getPanelId.createDelegate(this);
41524     this.box = null;
41525     this.activePanel = null;
41526     // ensure listeners are added...
41527     
41528     if (config.listeners || config.events) {
41529         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41530             listeners : config.listeners || {},
41531             events : config.events || {}
41532         });
41533     }
41534     
41535     if(skipConfig !== true){
41536         this.applyConfig(config);
41537     }
41538 };
41539
41540 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41541 {
41542     getPanelId : function(p){
41543         return p.getId();
41544     },
41545     
41546     applyConfig : function(config){
41547         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41548         this.config = config;
41549         
41550     },
41551     
41552     /**
41553      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41554      * the width, for horizontal (north, south) the height.
41555      * @param {Number} newSize The new width or height
41556      */
41557     resizeTo : function(newSize){
41558         var el = this.el ? this.el :
41559                  (this.activePanel ? this.activePanel.getEl() : null);
41560         if(el){
41561             switch(this.position){
41562                 case "east":
41563                 case "west":
41564                     el.setWidth(newSize);
41565                     this.fireEvent("resized", this, newSize);
41566                 break;
41567                 case "north":
41568                 case "south":
41569                     el.setHeight(newSize);
41570                     this.fireEvent("resized", this, newSize);
41571                 break;                
41572             }
41573         }
41574     },
41575     
41576     getBox : function(){
41577         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41578     },
41579     
41580     getMargins : function(){
41581         return this.margins;
41582     },
41583     
41584     updateBox : function(box){
41585         this.box = box;
41586         var el = this.activePanel.getEl();
41587         el.dom.style.left = box.x + "px";
41588         el.dom.style.top = box.y + "px";
41589         this.activePanel.setSize(box.width, box.height);
41590     },
41591     
41592     /**
41593      * Returns the container element for this region.
41594      * @return {Roo.Element}
41595      */
41596     getEl : function(){
41597         return this.activePanel;
41598     },
41599     
41600     /**
41601      * Returns true if this region is currently visible.
41602      * @return {Boolean}
41603      */
41604     isVisible : function(){
41605         return this.activePanel ? true : false;
41606     },
41607     
41608     setActivePanel : function(panel){
41609         panel = this.getPanel(panel);
41610         if(this.activePanel && this.activePanel != panel){
41611             this.activePanel.setActiveState(false);
41612             this.activePanel.getEl().setLeftTop(-10000,-10000);
41613         }
41614         this.activePanel = panel;
41615         panel.setActiveState(true);
41616         if(this.box){
41617             panel.setSize(this.box.width, this.box.height);
41618         }
41619         this.fireEvent("panelactivated", this, panel);
41620         this.fireEvent("invalidated");
41621     },
41622     
41623     /**
41624      * Show the specified panel.
41625      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41626      * @return {Roo.ContentPanel} The shown panel or null
41627      */
41628     showPanel : function(panel){
41629         panel = this.getPanel(panel);
41630         if(panel){
41631             this.setActivePanel(panel);
41632         }
41633         return panel;
41634     },
41635     
41636     /**
41637      * Get the active panel for this region.
41638      * @return {Roo.ContentPanel} The active panel or null
41639      */
41640     getActivePanel : function(){
41641         return this.activePanel;
41642     },
41643     
41644     /**
41645      * Add the passed ContentPanel(s)
41646      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41647      * @return {Roo.ContentPanel} The panel added (if only one was added)
41648      */
41649     add : function(panel){
41650         if(arguments.length > 1){
41651             for(var i = 0, len = arguments.length; i < len; i++) {
41652                 this.add(arguments[i]);
41653             }
41654             return null;
41655         }
41656         if(this.hasPanel(panel)){
41657             this.showPanel(panel);
41658             return panel;
41659         }
41660         var el = panel.getEl();
41661         if(el.dom.parentNode != this.mgr.el.dom){
41662             this.mgr.el.dom.appendChild(el.dom);
41663         }
41664         if(panel.setRegion){
41665             panel.setRegion(this);
41666         }
41667         this.panels.add(panel);
41668         el.setStyle("position", "absolute");
41669         if(!panel.background){
41670             this.setActivePanel(panel);
41671             if(this.config.initialSize && this.panels.getCount()==1){
41672                 this.resizeTo(this.config.initialSize);
41673             }
41674         }
41675         this.fireEvent("paneladded", this, panel);
41676         return panel;
41677     },
41678     
41679     /**
41680      * Returns true if the panel is in this region.
41681      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41682      * @return {Boolean}
41683      */
41684     hasPanel : function(panel){
41685         if(typeof panel == "object"){ // must be panel obj
41686             panel = panel.getId();
41687         }
41688         return this.getPanel(panel) ? true : false;
41689     },
41690     
41691     /**
41692      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41693      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41694      * @param {Boolean} preservePanel Overrides the config preservePanel option
41695      * @return {Roo.ContentPanel} The panel that was removed
41696      */
41697     remove : function(panel, preservePanel){
41698         panel = this.getPanel(panel);
41699         if(!panel){
41700             return null;
41701         }
41702         var e = {};
41703         this.fireEvent("beforeremove", this, panel, e);
41704         if(e.cancel === true){
41705             return null;
41706         }
41707         var panelId = panel.getId();
41708         this.panels.removeKey(panelId);
41709         return panel;
41710     },
41711     
41712     /**
41713      * Returns the panel specified or null if it's not in this region.
41714      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41715      * @return {Roo.ContentPanel}
41716      */
41717     getPanel : function(id){
41718         if(typeof id == "object"){ // must be panel obj
41719             return id;
41720         }
41721         return this.panels.get(id);
41722     },
41723     
41724     /**
41725      * Returns this regions position (north/south/east/west/center).
41726      * @return {String} 
41727      */
41728     getPosition: function(){
41729         return this.position;    
41730     }
41731 });/*
41732  * Based on:
41733  * Ext JS Library 1.1.1
41734  * Copyright(c) 2006-2007, Ext JS, LLC.
41735  *
41736  * Originally Released Under LGPL - original licence link has changed is not relivant.
41737  *
41738  * Fork - LGPL
41739  * <script type="text/javascript">
41740  */
41741  
41742 /**
41743  * @class Roo.bootstrap.layout.Region
41744  * @extends Roo.bootstrap.layout.Basic
41745  * This class represents a region in a layout manager.
41746  
41747  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41748  * @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})
41749  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41750  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41751  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41752  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41753  * @cfg {String}    title           The title for the region (overrides panel titles)
41754  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41755  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41756  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41757  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41758  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41759  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41760  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41761  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41762  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41763  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41764
41765  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41766  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41767  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41768  * @cfg {Number}    width           For East/West panels
41769  * @cfg {Number}    height          For North/South panels
41770  * @cfg {Boolean}   split           To show the splitter
41771  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41772  * 
41773  * @cfg {string}   cls             Extra CSS classes to add to region
41774  * 
41775  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41776  * @cfg {string}   region  the region that it inhabits..
41777  *
41778
41779  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41780  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41781
41782  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41783  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41784  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41785  */
41786 Roo.bootstrap.layout.Region = function(config)
41787 {
41788     this.applyConfig(config);
41789
41790     var mgr = config.mgr;
41791     var pos = config.region;
41792     config.skipConfig = true;
41793     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41794     
41795     if (mgr.el) {
41796         this.onRender(mgr.el);   
41797     }
41798      
41799     this.visible = true;
41800     this.collapsed = false;
41801     this.unrendered_panels = [];
41802 };
41803
41804 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41805
41806     position: '', // set by wrapper (eg. north/south etc..)
41807     unrendered_panels : null,  // unrendered panels.
41808     
41809     tabPosition : false,
41810     
41811     mgr: false, // points to 'Border'
41812     
41813     
41814     createBody : function(){
41815         /** This region's body element 
41816         * @type Roo.Element */
41817         this.bodyEl = this.el.createChild({
41818                 tag: "div",
41819                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41820         });
41821     },
41822
41823     onRender: function(ctr, pos)
41824     {
41825         var dh = Roo.DomHelper;
41826         /** This region's container element 
41827         * @type Roo.Element */
41828         this.el = dh.append(ctr.dom, {
41829                 tag: "div",
41830                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41831             }, true);
41832         /** This region's title element 
41833         * @type Roo.Element */
41834     
41835         this.titleEl = dh.append(this.el.dom,  {
41836                 tag: "div",
41837                 unselectable: "on",
41838                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41839                 children:[
41840                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41841                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41842                 ]
41843             }, true);
41844         
41845         this.titleEl.enableDisplayMode();
41846         /** This region's title text element 
41847         * @type HTMLElement */
41848         this.titleTextEl = this.titleEl.dom.firstChild;
41849         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41850         /*
41851         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41852         this.closeBtn.enableDisplayMode();
41853         this.closeBtn.on("click", this.closeClicked, this);
41854         this.closeBtn.hide();
41855     */
41856         this.createBody(this.config);
41857         if(this.config.hideWhenEmpty){
41858             this.hide();
41859             this.on("paneladded", this.validateVisibility, this);
41860             this.on("panelremoved", this.validateVisibility, this);
41861         }
41862         if(this.autoScroll){
41863             this.bodyEl.setStyle("overflow", "auto");
41864         }else{
41865             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41866         }
41867         //if(c.titlebar !== false){
41868             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41869                 this.titleEl.hide();
41870             }else{
41871                 this.titleEl.show();
41872                 if(this.config.title){
41873                     this.titleTextEl.innerHTML = this.config.title;
41874                 }
41875             }
41876         //}
41877         if(this.config.collapsed){
41878             this.collapse(true);
41879         }
41880         if(this.config.hidden){
41881             this.hide();
41882         }
41883         
41884         if (this.unrendered_panels && this.unrendered_panels.length) {
41885             for (var i =0;i< this.unrendered_panels.length; i++) {
41886                 this.add(this.unrendered_panels[i]);
41887             }
41888             this.unrendered_panels = null;
41889             
41890         }
41891         
41892     },
41893     
41894     applyConfig : function(c)
41895     {
41896         /*
41897          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41898             var dh = Roo.DomHelper;
41899             if(c.titlebar !== false){
41900                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41901                 this.collapseBtn.on("click", this.collapse, this);
41902                 this.collapseBtn.enableDisplayMode();
41903                 /*
41904                 if(c.showPin === true || this.showPin){
41905                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41906                     this.stickBtn.enableDisplayMode();
41907                     this.stickBtn.on("click", this.expand, this);
41908                     this.stickBtn.hide();
41909                 }
41910                 
41911             }
41912             */
41913             /** This region's collapsed element
41914             * @type Roo.Element */
41915             /*
41916              *
41917             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41918                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41919             ]}, true);
41920             
41921             if(c.floatable !== false){
41922                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41923                this.collapsedEl.on("click", this.collapseClick, this);
41924             }
41925
41926             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41927                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41928                    id: "message", unselectable: "on", style:{"float":"left"}});
41929                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41930              }
41931             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41932             this.expandBtn.on("click", this.expand, this);
41933             
41934         }
41935         
41936         if(this.collapseBtn){
41937             this.collapseBtn.setVisible(c.collapsible == true);
41938         }
41939         
41940         this.cmargins = c.cmargins || this.cmargins ||
41941                          (this.position == "west" || this.position == "east" ?
41942                              {top: 0, left: 2, right:2, bottom: 0} :
41943                              {top: 2, left: 0, right:0, bottom: 2});
41944         */
41945         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41946         
41947         
41948         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41949         
41950         this.autoScroll = c.autoScroll || false;
41951         
41952         
41953        
41954         
41955         this.duration = c.duration || .30;
41956         this.slideDuration = c.slideDuration || .45;
41957         this.config = c;
41958        
41959     },
41960     /**
41961      * Returns true if this region is currently visible.
41962      * @return {Boolean}
41963      */
41964     isVisible : function(){
41965         return this.visible;
41966     },
41967
41968     /**
41969      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41970      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41971      */
41972     //setCollapsedTitle : function(title){
41973     //    title = title || "&#160;";
41974      //   if(this.collapsedTitleTextEl){
41975       //      this.collapsedTitleTextEl.innerHTML = title;
41976        // }
41977     //},
41978
41979     getBox : function(){
41980         var b;
41981       //  if(!this.collapsed){
41982             b = this.el.getBox(false, true);
41983        // }else{
41984           //  b = this.collapsedEl.getBox(false, true);
41985         //}
41986         return b;
41987     },
41988
41989     getMargins : function(){
41990         return this.margins;
41991         //return this.collapsed ? this.cmargins : this.margins;
41992     },
41993 /*
41994     highlight : function(){
41995         this.el.addClass("x-layout-panel-dragover");
41996     },
41997
41998     unhighlight : function(){
41999         this.el.removeClass("x-layout-panel-dragover");
42000     },
42001 */
42002     updateBox : function(box)
42003     {
42004         if (!this.bodyEl) {
42005             return; // not rendered yet..
42006         }
42007         
42008         this.box = box;
42009         if(!this.collapsed){
42010             this.el.dom.style.left = box.x + "px";
42011             this.el.dom.style.top = box.y + "px";
42012             this.updateBody(box.width, box.height);
42013         }else{
42014             this.collapsedEl.dom.style.left = box.x + "px";
42015             this.collapsedEl.dom.style.top = box.y + "px";
42016             this.collapsedEl.setSize(box.width, box.height);
42017         }
42018         if(this.tabs){
42019             this.tabs.autoSizeTabs();
42020         }
42021     },
42022
42023     updateBody : function(w, h)
42024     {
42025         if(w !== null){
42026             this.el.setWidth(w);
42027             w -= this.el.getBorderWidth("rl");
42028             if(this.config.adjustments){
42029                 w += this.config.adjustments[0];
42030             }
42031         }
42032         if(h !== null && h > 0){
42033             this.el.setHeight(h);
42034             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42035             h -= this.el.getBorderWidth("tb");
42036             if(this.config.adjustments){
42037                 h += this.config.adjustments[1];
42038             }
42039             this.bodyEl.setHeight(h);
42040             if(this.tabs){
42041                 h = this.tabs.syncHeight(h);
42042             }
42043         }
42044         if(this.panelSize){
42045             w = w !== null ? w : this.panelSize.width;
42046             h = h !== null ? h : this.panelSize.height;
42047         }
42048         if(this.activePanel){
42049             var el = this.activePanel.getEl();
42050             w = w !== null ? w : el.getWidth();
42051             h = h !== null ? h : el.getHeight();
42052             this.panelSize = {width: w, height: h};
42053             this.activePanel.setSize(w, h);
42054         }
42055         if(Roo.isIE && this.tabs){
42056             this.tabs.el.repaint();
42057         }
42058     },
42059
42060     /**
42061      * Returns the container element for this region.
42062      * @return {Roo.Element}
42063      */
42064     getEl : function(){
42065         return this.el;
42066     },
42067
42068     /**
42069      * Hides this region.
42070      */
42071     hide : function(){
42072         //if(!this.collapsed){
42073             this.el.dom.style.left = "-2000px";
42074             this.el.hide();
42075         //}else{
42076          //   this.collapsedEl.dom.style.left = "-2000px";
42077          //   this.collapsedEl.hide();
42078        // }
42079         this.visible = false;
42080         this.fireEvent("visibilitychange", this, false);
42081     },
42082
42083     /**
42084      * Shows this region if it was previously hidden.
42085      */
42086     show : function(){
42087         //if(!this.collapsed){
42088             this.el.show();
42089         //}else{
42090         //    this.collapsedEl.show();
42091        // }
42092         this.visible = true;
42093         this.fireEvent("visibilitychange", this, true);
42094     },
42095 /*
42096     closeClicked : function(){
42097         if(this.activePanel){
42098             this.remove(this.activePanel);
42099         }
42100     },
42101
42102     collapseClick : function(e){
42103         if(this.isSlid){
42104            e.stopPropagation();
42105            this.slideIn();
42106         }else{
42107            e.stopPropagation();
42108            this.slideOut();
42109         }
42110     },
42111 */
42112     /**
42113      * Collapses this region.
42114      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42115      */
42116     /*
42117     collapse : function(skipAnim, skipCheck = false){
42118         if(this.collapsed) {
42119             return;
42120         }
42121         
42122         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42123             
42124             this.collapsed = true;
42125             if(this.split){
42126                 this.split.el.hide();
42127             }
42128             if(this.config.animate && skipAnim !== true){
42129                 this.fireEvent("invalidated", this);
42130                 this.animateCollapse();
42131             }else{
42132                 this.el.setLocation(-20000,-20000);
42133                 this.el.hide();
42134                 this.collapsedEl.show();
42135                 this.fireEvent("collapsed", this);
42136                 this.fireEvent("invalidated", this);
42137             }
42138         }
42139         
42140     },
42141 */
42142     animateCollapse : function(){
42143         // overridden
42144     },
42145
42146     /**
42147      * Expands this region if it was previously collapsed.
42148      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42149      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42150      */
42151     /*
42152     expand : function(e, skipAnim){
42153         if(e) {
42154             e.stopPropagation();
42155         }
42156         if(!this.collapsed || this.el.hasActiveFx()) {
42157             return;
42158         }
42159         if(this.isSlid){
42160             this.afterSlideIn();
42161             skipAnim = true;
42162         }
42163         this.collapsed = false;
42164         if(this.config.animate && skipAnim !== true){
42165             this.animateExpand();
42166         }else{
42167             this.el.show();
42168             if(this.split){
42169                 this.split.el.show();
42170             }
42171             this.collapsedEl.setLocation(-2000,-2000);
42172             this.collapsedEl.hide();
42173             this.fireEvent("invalidated", this);
42174             this.fireEvent("expanded", this);
42175         }
42176     },
42177 */
42178     animateExpand : function(){
42179         // overridden
42180     },
42181
42182     initTabs : function()
42183     {
42184         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42185         
42186         var ts = new Roo.bootstrap.panel.Tabs({
42187             el: this.bodyEl.dom,
42188             region : this,
42189             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42190             disableTooltips: this.config.disableTabTips,
42191             toolbar : this.config.toolbar
42192         });
42193         
42194         if(this.config.hideTabs){
42195             ts.stripWrap.setDisplayed(false);
42196         }
42197         this.tabs = ts;
42198         ts.resizeTabs = this.config.resizeTabs === true;
42199         ts.minTabWidth = this.config.minTabWidth || 40;
42200         ts.maxTabWidth = this.config.maxTabWidth || 250;
42201         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42202         ts.monitorResize = false;
42203         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42204         ts.bodyEl.addClass('roo-layout-tabs-body');
42205         this.panels.each(this.initPanelAsTab, this);
42206     },
42207
42208     initPanelAsTab : function(panel){
42209         var ti = this.tabs.addTab(
42210             panel.getEl().id,
42211             panel.getTitle(),
42212             null,
42213             this.config.closeOnTab && panel.isClosable(),
42214             panel.tpl
42215         );
42216         if(panel.tabTip !== undefined){
42217             ti.setTooltip(panel.tabTip);
42218         }
42219         ti.on("activate", function(){
42220               this.setActivePanel(panel);
42221         }, this);
42222         
42223         if(this.config.closeOnTab){
42224             ti.on("beforeclose", function(t, e){
42225                 e.cancel = true;
42226                 this.remove(panel);
42227             }, this);
42228         }
42229         
42230         panel.tabItem = ti;
42231         
42232         return ti;
42233     },
42234
42235     updatePanelTitle : function(panel, title)
42236     {
42237         if(this.activePanel == panel){
42238             this.updateTitle(title);
42239         }
42240         if(this.tabs){
42241             var ti = this.tabs.getTab(panel.getEl().id);
42242             ti.setText(title);
42243             if(panel.tabTip !== undefined){
42244                 ti.setTooltip(panel.tabTip);
42245             }
42246         }
42247     },
42248
42249     updateTitle : function(title){
42250         if(this.titleTextEl && !this.config.title){
42251             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42252         }
42253     },
42254
42255     setActivePanel : function(panel)
42256     {
42257         panel = this.getPanel(panel);
42258         if(this.activePanel && this.activePanel != panel){
42259             if(this.activePanel.setActiveState(false) === false){
42260                 return;
42261             }
42262         }
42263         this.activePanel = panel;
42264         panel.setActiveState(true);
42265         if(this.panelSize){
42266             panel.setSize(this.panelSize.width, this.panelSize.height);
42267         }
42268         if(this.closeBtn){
42269             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42270         }
42271         this.updateTitle(panel.getTitle());
42272         if(this.tabs){
42273             this.fireEvent("invalidated", this);
42274         }
42275         this.fireEvent("panelactivated", this, panel);
42276     },
42277
42278     /**
42279      * Shows the specified panel.
42280      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42281      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42282      */
42283     showPanel : function(panel)
42284     {
42285         panel = this.getPanel(panel);
42286         if(panel){
42287             if(this.tabs){
42288                 var tab = this.tabs.getTab(panel.getEl().id);
42289                 if(tab.isHidden()){
42290                     this.tabs.unhideTab(tab.id);
42291                 }
42292                 tab.activate();
42293             }else{
42294                 this.setActivePanel(panel);
42295             }
42296         }
42297         return panel;
42298     },
42299
42300     /**
42301      * Get the active panel for this region.
42302      * @return {Roo.ContentPanel} The active panel or null
42303      */
42304     getActivePanel : function(){
42305         return this.activePanel;
42306     },
42307
42308     validateVisibility : function(){
42309         if(this.panels.getCount() < 1){
42310             this.updateTitle("&#160;");
42311             this.closeBtn.hide();
42312             this.hide();
42313         }else{
42314             if(!this.isVisible()){
42315                 this.show();
42316             }
42317         }
42318     },
42319
42320     /**
42321      * Adds the passed ContentPanel(s) to this region.
42322      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42323      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42324      */
42325     add : function(panel)
42326     {
42327         if(arguments.length > 1){
42328             for(var i = 0, len = arguments.length; i < len; i++) {
42329                 this.add(arguments[i]);
42330             }
42331             return null;
42332         }
42333         
42334         // if we have not been rendered yet, then we can not really do much of this..
42335         if (!this.bodyEl) {
42336             this.unrendered_panels.push(panel);
42337             return panel;
42338         }
42339         
42340         
42341         
42342         
42343         if(this.hasPanel(panel)){
42344             this.showPanel(panel);
42345             return panel;
42346         }
42347         panel.setRegion(this);
42348         this.panels.add(panel);
42349        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42350             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42351             // and hide them... ???
42352             this.bodyEl.dom.appendChild(panel.getEl().dom);
42353             if(panel.background !== true){
42354                 this.setActivePanel(panel);
42355             }
42356             this.fireEvent("paneladded", this, panel);
42357             return panel;
42358         }
42359         */
42360         if(!this.tabs){
42361             this.initTabs();
42362         }else{
42363             this.initPanelAsTab(panel);
42364         }
42365         
42366         
42367         if(panel.background !== true){
42368             this.tabs.activate(panel.getEl().id);
42369         }
42370         this.fireEvent("paneladded", this, panel);
42371         return panel;
42372     },
42373
42374     /**
42375      * Hides the tab for the specified panel.
42376      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42377      */
42378     hidePanel : function(panel){
42379         if(this.tabs && (panel = this.getPanel(panel))){
42380             this.tabs.hideTab(panel.getEl().id);
42381         }
42382     },
42383
42384     /**
42385      * Unhides the tab for a previously hidden panel.
42386      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42387      */
42388     unhidePanel : function(panel){
42389         if(this.tabs && (panel = this.getPanel(panel))){
42390             this.tabs.unhideTab(panel.getEl().id);
42391         }
42392     },
42393
42394     clearPanels : function(){
42395         while(this.panels.getCount() > 0){
42396              this.remove(this.panels.first());
42397         }
42398     },
42399
42400     /**
42401      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42402      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42403      * @param {Boolean} preservePanel Overrides the config preservePanel option
42404      * @return {Roo.ContentPanel} The panel that was removed
42405      */
42406     remove : function(panel, preservePanel)
42407     {
42408         panel = this.getPanel(panel);
42409         if(!panel){
42410             return null;
42411         }
42412         var e = {};
42413         this.fireEvent("beforeremove", this, panel, e);
42414         if(e.cancel === true){
42415             return null;
42416         }
42417         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42418         var panelId = panel.getId();
42419         this.panels.removeKey(panelId);
42420         if(preservePanel){
42421             document.body.appendChild(panel.getEl().dom);
42422         }
42423         if(this.tabs){
42424             this.tabs.removeTab(panel.getEl().id);
42425         }else if (!preservePanel){
42426             this.bodyEl.dom.removeChild(panel.getEl().dom);
42427         }
42428         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42429             var p = this.panels.first();
42430             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42431             tempEl.appendChild(p.getEl().dom);
42432             this.bodyEl.update("");
42433             this.bodyEl.dom.appendChild(p.getEl().dom);
42434             tempEl = null;
42435             this.updateTitle(p.getTitle());
42436             this.tabs = null;
42437             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42438             this.setActivePanel(p);
42439         }
42440         panel.setRegion(null);
42441         if(this.activePanel == panel){
42442             this.activePanel = null;
42443         }
42444         if(this.config.autoDestroy !== false && preservePanel !== true){
42445             try{panel.destroy();}catch(e){}
42446         }
42447         this.fireEvent("panelremoved", this, panel);
42448         return panel;
42449     },
42450
42451     /**
42452      * Returns the TabPanel component used by this region
42453      * @return {Roo.TabPanel}
42454      */
42455     getTabs : function(){
42456         return this.tabs;
42457     },
42458
42459     createTool : function(parentEl, className){
42460         var btn = Roo.DomHelper.append(parentEl, {
42461             tag: "div",
42462             cls: "x-layout-tools-button",
42463             children: [ {
42464                 tag: "div",
42465                 cls: "roo-layout-tools-button-inner " + className,
42466                 html: "&#160;"
42467             }]
42468         }, true);
42469         btn.addClassOnOver("roo-layout-tools-button-over");
42470         return btn;
42471     }
42472 });/*
42473  * Based on:
42474  * Ext JS Library 1.1.1
42475  * Copyright(c) 2006-2007, Ext JS, LLC.
42476  *
42477  * Originally Released Under LGPL - original licence link has changed is not relivant.
42478  *
42479  * Fork - LGPL
42480  * <script type="text/javascript">
42481  */
42482  
42483
42484
42485 /**
42486  * @class Roo.SplitLayoutRegion
42487  * @extends Roo.LayoutRegion
42488  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42489  */
42490 Roo.bootstrap.layout.Split = function(config){
42491     this.cursor = config.cursor;
42492     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42493 };
42494
42495 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42496 {
42497     splitTip : "Drag to resize.",
42498     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42499     useSplitTips : false,
42500
42501     applyConfig : function(config){
42502         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42503     },
42504     
42505     onRender : function(ctr,pos) {
42506         
42507         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42508         if(!this.config.split){
42509             return;
42510         }
42511         if(!this.split){
42512             
42513             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42514                             tag: "div",
42515                             id: this.el.id + "-split",
42516                             cls: "roo-layout-split roo-layout-split-"+this.position,
42517                             html: "&#160;"
42518             });
42519             /** The SplitBar for this region 
42520             * @type Roo.SplitBar */
42521             // does not exist yet...
42522             Roo.log([this.position, this.orientation]);
42523             
42524             this.split = new Roo.bootstrap.SplitBar({
42525                 dragElement : splitEl,
42526                 resizingElement: this.el,
42527                 orientation : this.orientation
42528             });
42529             
42530             this.split.on("moved", this.onSplitMove, this);
42531             this.split.useShim = this.config.useShim === true;
42532             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42533             if(this.useSplitTips){
42534                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42535             }
42536             //if(config.collapsible){
42537             //    this.split.el.on("dblclick", this.collapse,  this);
42538             //}
42539         }
42540         if(typeof this.config.minSize != "undefined"){
42541             this.split.minSize = this.config.minSize;
42542         }
42543         if(typeof this.config.maxSize != "undefined"){
42544             this.split.maxSize = this.config.maxSize;
42545         }
42546         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42547             this.hideSplitter();
42548         }
42549         
42550     },
42551
42552     getHMaxSize : function(){
42553          var cmax = this.config.maxSize || 10000;
42554          var center = this.mgr.getRegion("center");
42555          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42556     },
42557
42558     getVMaxSize : function(){
42559          var cmax = this.config.maxSize || 10000;
42560          var center = this.mgr.getRegion("center");
42561          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42562     },
42563
42564     onSplitMove : function(split, newSize){
42565         this.fireEvent("resized", this, newSize);
42566     },
42567     
42568     /** 
42569      * Returns the {@link Roo.SplitBar} for this region.
42570      * @return {Roo.SplitBar}
42571      */
42572     getSplitBar : function(){
42573         return this.split;
42574     },
42575     
42576     hide : function(){
42577         this.hideSplitter();
42578         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42579     },
42580
42581     hideSplitter : function(){
42582         if(this.split){
42583             this.split.el.setLocation(-2000,-2000);
42584             this.split.el.hide();
42585         }
42586     },
42587
42588     show : function(){
42589         if(this.split){
42590             this.split.el.show();
42591         }
42592         Roo.bootstrap.layout.Split.superclass.show.call(this);
42593     },
42594     
42595     beforeSlide: function(){
42596         if(Roo.isGecko){// firefox overflow auto bug workaround
42597             this.bodyEl.clip();
42598             if(this.tabs) {
42599                 this.tabs.bodyEl.clip();
42600             }
42601             if(this.activePanel){
42602                 this.activePanel.getEl().clip();
42603                 
42604                 if(this.activePanel.beforeSlide){
42605                     this.activePanel.beforeSlide();
42606                 }
42607             }
42608         }
42609     },
42610     
42611     afterSlide : function(){
42612         if(Roo.isGecko){// firefox overflow auto bug workaround
42613             this.bodyEl.unclip();
42614             if(this.tabs) {
42615                 this.tabs.bodyEl.unclip();
42616             }
42617             if(this.activePanel){
42618                 this.activePanel.getEl().unclip();
42619                 if(this.activePanel.afterSlide){
42620                     this.activePanel.afterSlide();
42621                 }
42622             }
42623         }
42624     },
42625
42626     initAutoHide : function(){
42627         if(this.autoHide !== false){
42628             if(!this.autoHideHd){
42629                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42630                 this.autoHideHd = {
42631                     "mouseout": function(e){
42632                         if(!e.within(this.el, true)){
42633                             st.delay(500);
42634                         }
42635                     },
42636                     "mouseover" : function(e){
42637                         st.cancel();
42638                     },
42639                     scope : this
42640                 };
42641             }
42642             this.el.on(this.autoHideHd);
42643         }
42644     },
42645
42646     clearAutoHide : function(){
42647         if(this.autoHide !== false){
42648             this.el.un("mouseout", this.autoHideHd.mouseout);
42649             this.el.un("mouseover", this.autoHideHd.mouseover);
42650         }
42651     },
42652
42653     clearMonitor : function(){
42654         Roo.get(document).un("click", this.slideInIf, this);
42655     },
42656
42657     // these names are backwards but not changed for compat
42658     slideOut : function(){
42659         if(this.isSlid || this.el.hasActiveFx()){
42660             return;
42661         }
42662         this.isSlid = true;
42663         if(this.collapseBtn){
42664             this.collapseBtn.hide();
42665         }
42666         this.closeBtnState = this.closeBtn.getStyle('display');
42667         this.closeBtn.hide();
42668         if(this.stickBtn){
42669             this.stickBtn.show();
42670         }
42671         this.el.show();
42672         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42673         this.beforeSlide();
42674         this.el.setStyle("z-index", 10001);
42675         this.el.slideIn(this.getSlideAnchor(), {
42676             callback: function(){
42677                 this.afterSlide();
42678                 this.initAutoHide();
42679                 Roo.get(document).on("click", this.slideInIf, this);
42680                 this.fireEvent("slideshow", this);
42681             },
42682             scope: this,
42683             block: true
42684         });
42685     },
42686
42687     afterSlideIn : function(){
42688         this.clearAutoHide();
42689         this.isSlid = false;
42690         this.clearMonitor();
42691         this.el.setStyle("z-index", "");
42692         if(this.collapseBtn){
42693             this.collapseBtn.show();
42694         }
42695         this.closeBtn.setStyle('display', this.closeBtnState);
42696         if(this.stickBtn){
42697             this.stickBtn.hide();
42698         }
42699         this.fireEvent("slidehide", this);
42700     },
42701
42702     slideIn : function(cb){
42703         if(!this.isSlid || this.el.hasActiveFx()){
42704             Roo.callback(cb);
42705             return;
42706         }
42707         this.isSlid = false;
42708         this.beforeSlide();
42709         this.el.slideOut(this.getSlideAnchor(), {
42710             callback: function(){
42711                 this.el.setLeftTop(-10000, -10000);
42712                 this.afterSlide();
42713                 this.afterSlideIn();
42714                 Roo.callback(cb);
42715             },
42716             scope: this,
42717             block: true
42718         });
42719     },
42720     
42721     slideInIf : function(e){
42722         if(!e.within(this.el)){
42723             this.slideIn();
42724         }
42725     },
42726
42727     animateCollapse : function(){
42728         this.beforeSlide();
42729         this.el.setStyle("z-index", 20000);
42730         var anchor = this.getSlideAnchor();
42731         this.el.slideOut(anchor, {
42732             callback : function(){
42733                 this.el.setStyle("z-index", "");
42734                 this.collapsedEl.slideIn(anchor, {duration:.3});
42735                 this.afterSlide();
42736                 this.el.setLocation(-10000,-10000);
42737                 this.el.hide();
42738                 this.fireEvent("collapsed", this);
42739             },
42740             scope: this,
42741             block: true
42742         });
42743     },
42744
42745     animateExpand : function(){
42746         this.beforeSlide();
42747         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42748         this.el.setStyle("z-index", 20000);
42749         this.collapsedEl.hide({
42750             duration:.1
42751         });
42752         this.el.slideIn(this.getSlideAnchor(), {
42753             callback : function(){
42754                 this.el.setStyle("z-index", "");
42755                 this.afterSlide();
42756                 if(this.split){
42757                     this.split.el.show();
42758                 }
42759                 this.fireEvent("invalidated", this);
42760                 this.fireEvent("expanded", this);
42761             },
42762             scope: this,
42763             block: true
42764         });
42765     },
42766
42767     anchors : {
42768         "west" : "left",
42769         "east" : "right",
42770         "north" : "top",
42771         "south" : "bottom"
42772     },
42773
42774     sanchors : {
42775         "west" : "l",
42776         "east" : "r",
42777         "north" : "t",
42778         "south" : "b"
42779     },
42780
42781     canchors : {
42782         "west" : "tl-tr",
42783         "east" : "tr-tl",
42784         "north" : "tl-bl",
42785         "south" : "bl-tl"
42786     },
42787
42788     getAnchor : function(){
42789         return this.anchors[this.position];
42790     },
42791
42792     getCollapseAnchor : function(){
42793         return this.canchors[this.position];
42794     },
42795
42796     getSlideAnchor : function(){
42797         return this.sanchors[this.position];
42798     },
42799
42800     getAlignAdj : function(){
42801         var cm = this.cmargins;
42802         switch(this.position){
42803             case "west":
42804                 return [0, 0];
42805             break;
42806             case "east":
42807                 return [0, 0];
42808             break;
42809             case "north":
42810                 return [0, 0];
42811             break;
42812             case "south":
42813                 return [0, 0];
42814             break;
42815         }
42816     },
42817
42818     getExpandAdj : function(){
42819         var c = this.collapsedEl, cm = this.cmargins;
42820         switch(this.position){
42821             case "west":
42822                 return [-(cm.right+c.getWidth()+cm.left), 0];
42823             break;
42824             case "east":
42825                 return [cm.right+c.getWidth()+cm.left, 0];
42826             break;
42827             case "north":
42828                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42829             break;
42830             case "south":
42831                 return [0, cm.top+cm.bottom+c.getHeight()];
42832             break;
42833         }
42834     }
42835 });/*
42836  * Based on:
42837  * Ext JS Library 1.1.1
42838  * Copyright(c) 2006-2007, Ext JS, LLC.
42839  *
42840  * Originally Released Under LGPL - original licence link has changed is not relivant.
42841  *
42842  * Fork - LGPL
42843  * <script type="text/javascript">
42844  */
42845 /*
42846  * These classes are private internal classes
42847  */
42848 Roo.bootstrap.layout.Center = function(config){
42849     config.region = "center";
42850     Roo.bootstrap.layout.Region.call(this, config);
42851     this.visible = true;
42852     this.minWidth = config.minWidth || 20;
42853     this.minHeight = config.minHeight || 20;
42854 };
42855
42856 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42857     hide : function(){
42858         // center panel can't be hidden
42859     },
42860     
42861     show : function(){
42862         // center panel can't be hidden
42863     },
42864     
42865     getMinWidth: function(){
42866         return this.minWidth;
42867     },
42868     
42869     getMinHeight: function(){
42870         return this.minHeight;
42871     }
42872 });
42873
42874
42875
42876
42877  
42878
42879
42880
42881
42882
42883
42884 Roo.bootstrap.layout.North = function(config)
42885 {
42886     config.region = 'north';
42887     config.cursor = 'n-resize';
42888     
42889     Roo.bootstrap.layout.Split.call(this, config);
42890     
42891     
42892     if(this.split){
42893         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42894         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42895         this.split.el.addClass("roo-layout-split-v");
42896     }
42897     //var size = config.initialSize || config.height;
42898     //if(this.el && typeof size != "undefined"){
42899     //    this.el.setHeight(size);
42900     //}
42901 };
42902 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42903 {
42904     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42905      
42906      
42907     onRender : function(ctr, pos)
42908     {
42909         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42910         var size = this.config.initialSize || this.config.height;
42911         if(this.el && typeof size != "undefined"){
42912             this.el.setHeight(size);
42913         }
42914     
42915     },
42916     
42917     getBox : function(){
42918         if(this.collapsed){
42919             return this.collapsedEl.getBox();
42920         }
42921         var box = this.el.getBox();
42922         if(this.split){
42923             box.height += this.split.el.getHeight();
42924         }
42925         return box;
42926     },
42927     
42928     updateBox : function(box){
42929         if(this.split && !this.collapsed){
42930             box.height -= this.split.el.getHeight();
42931             this.split.el.setLeft(box.x);
42932             this.split.el.setTop(box.y+box.height);
42933             this.split.el.setWidth(box.width);
42934         }
42935         if(this.collapsed){
42936             this.updateBody(box.width, null);
42937         }
42938         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42939     }
42940 });
42941
42942
42943
42944
42945
42946 Roo.bootstrap.layout.South = function(config){
42947     config.region = 'south';
42948     config.cursor = 's-resize';
42949     Roo.bootstrap.layout.Split.call(this, config);
42950     if(this.split){
42951         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42952         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42953         this.split.el.addClass("roo-layout-split-v");
42954     }
42955     
42956 };
42957
42958 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42959     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42960     
42961     onRender : function(ctr, pos)
42962     {
42963         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42964         var size = this.config.initialSize || this.config.height;
42965         if(this.el && typeof size != "undefined"){
42966             this.el.setHeight(size);
42967         }
42968     
42969     },
42970     
42971     getBox : function(){
42972         if(this.collapsed){
42973             return this.collapsedEl.getBox();
42974         }
42975         var box = this.el.getBox();
42976         if(this.split){
42977             var sh = this.split.el.getHeight();
42978             box.height += sh;
42979             box.y -= sh;
42980         }
42981         return box;
42982     },
42983     
42984     updateBox : function(box){
42985         if(this.split && !this.collapsed){
42986             var sh = this.split.el.getHeight();
42987             box.height -= sh;
42988             box.y += sh;
42989             this.split.el.setLeft(box.x);
42990             this.split.el.setTop(box.y-sh);
42991             this.split.el.setWidth(box.width);
42992         }
42993         if(this.collapsed){
42994             this.updateBody(box.width, null);
42995         }
42996         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42997     }
42998 });
42999
43000 Roo.bootstrap.layout.East = function(config){
43001     config.region = "east";
43002     config.cursor = "e-resize";
43003     Roo.bootstrap.layout.Split.call(this, config);
43004     if(this.split){
43005         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
43006         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43007         this.split.el.addClass("roo-layout-split-h");
43008     }
43009     
43010 };
43011 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43012     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43013     
43014     onRender : function(ctr, pos)
43015     {
43016         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43017         var size = this.config.initialSize || this.config.width;
43018         if(this.el && typeof size != "undefined"){
43019             this.el.setWidth(size);
43020         }
43021     
43022     },
43023     
43024     getBox : function(){
43025         if(this.collapsed){
43026             return this.collapsedEl.getBox();
43027         }
43028         var box = this.el.getBox();
43029         if(this.split){
43030             var sw = this.split.el.getWidth();
43031             box.width += sw;
43032             box.x -= sw;
43033         }
43034         return box;
43035     },
43036
43037     updateBox : function(box){
43038         if(this.split && !this.collapsed){
43039             var sw = this.split.el.getWidth();
43040             box.width -= sw;
43041             this.split.el.setLeft(box.x);
43042             this.split.el.setTop(box.y);
43043             this.split.el.setHeight(box.height);
43044             box.x += sw;
43045         }
43046         if(this.collapsed){
43047             this.updateBody(null, box.height);
43048         }
43049         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43050     }
43051 });
43052
43053 Roo.bootstrap.layout.West = function(config){
43054     config.region = "west";
43055     config.cursor = "w-resize";
43056     
43057     Roo.bootstrap.layout.Split.call(this, config);
43058     if(this.split){
43059         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43060         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43061         this.split.el.addClass("roo-layout-split-h");
43062     }
43063     
43064 };
43065 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43066     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43067     
43068     onRender: function(ctr, pos)
43069     {
43070         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43071         var size = this.config.initialSize || this.config.width;
43072         if(typeof size != "undefined"){
43073             this.el.setWidth(size);
43074         }
43075     },
43076     
43077     getBox : function(){
43078         if(this.collapsed){
43079             return this.collapsedEl.getBox();
43080         }
43081         var box = this.el.getBox();
43082         if (box.width == 0) {
43083             box.width = this.config.width; // kludge?
43084         }
43085         if(this.split){
43086             box.width += this.split.el.getWidth();
43087         }
43088         return box;
43089     },
43090     
43091     updateBox : function(box){
43092         if(this.split && !this.collapsed){
43093             var sw = this.split.el.getWidth();
43094             box.width -= sw;
43095             this.split.el.setLeft(box.x+box.width);
43096             this.split.el.setTop(box.y);
43097             this.split.el.setHeight(box.height);
43098         }
43099         if(this.collapsed){
43100             this.updateBody(null, box.height);
43101         }
43102         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43103     }
43104 });/*
43105  * Based on:
43106  * Ext JS Library 1.1.1
43107  * Copyright(c) 2006-2007, Ext JS, LLC.
43108  *
43109  * Originally Released Under LGPL - original licence link has changed is not relivant.
43110  *
43111  * Fork - LGPL
43112  * <script type="text/javascript">
43113  */
43114 /**
43115  * @class Roo.bootstrap.paenl.Content
43116  * @extends Roo.util.Observable
43117  * @children Roo.bootstrap.Component
43118  * @parent builder Roo.bootstrap.layout.Border
43119  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43120  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43121  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43122  * @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
43123  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43124  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43125  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43126  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43127  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43128  * @cfg {String} title          The title for this panel
43129  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43130  * @cfg {String} url            Calls {@link #setUrl} with this value
43131  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43132  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43133  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43134  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43135  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43136  * @cfg {Boolean} badges render the badges
43137  * @cfg {String} cls  extra classes to use  
43138  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43139  
43140  * @constructor
43141  * Create a new ContentPanel.
43142  * @param {String/Object} config A string to set only the title or a config object
43143  
43144  */
43145 Roo.bootstrap.panel.Content = function( config){
43146     
43147     this.tpl = config.tpl || false;
43148     
43149     var el = config.el;
43150     var content = config.content;
43151
43152     if(config.autoCreate){ // xtype is available if this is called from factory
43153         el = Roo.id();
43154     }
43155     this.el = Roo.get(el);
43156     if(!this.el && config && config.autoCreate){
43157         if(typeof config.autoCreate == "object"){
43158             if(!config.autoCreate.id){
43159                 config.autoCreate.id = config.id||el;
43160             }
43161             this.el = Roo.DomHelper.append(document.body,
43162                         config.autoCreate, true);
43163         }else{
43164             var elcfg =  {
43165                 tag: "div",
43166                 cls: (config.cls || '') +
43167                     (config.background ? ' bg-' + config.background : '') +
43168                     " roo-layout-inactive-content",
43169                 id: config.id||el
43170             };
43171             if (config.iframe) {
43172                 elcfg.cn = [
43173                     {
43174                         tag : 'iframe',
43175                         style : 'border: 0px',
43176                         src : 'about:blank'
43177                     }
43178                 ];
43179             }
43180               
43181             if (config.html) {
43182                 elcfg.html = config.html;
43183                 
43184             }
43185                         
43186             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43187             if (config.iframe) {
43188                 this.iframeEl = this.el.select('iframe',true).first();
43189             }
43190             
43191         }
43192     } 
43193     this.closable = false;
43194     this.loaded = false;
43195     this.active = false;
43196    
43197       
43198     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43199         
43200         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43201         
43202         this.wrapEl = this.el; //this.el.wrap();
43203         var ti = [];
43204         if (config.toolbar.items) {
43205             ti = config.toolbar.items ;
43206             delete config.toolbar.items ;
43207         }
43208         
43209         var nitems = [];
43210         this.toolbar.render(this.wrapEl, 'before');
43211         for(var i =0;i < ti.length;i++) {
43212           //  Roo.log(['add child', items[i]]);
43213             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43214         }
43215         this.toolbar.items = nitems;
43216         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43217         delete config.toolbar;
43218         
43219     }
43220     /*
43221     // xtype created footer. - not sure if will work as we normally have to render first..
43222     if (this.footer && !this.footer.el && this.footer.xtype) {
43223         if (!this.wrapEl) {
43224             this.wrapEl = this.el.wrap();
43225         }
43226     
43227         this.footer.container = this.wrapEl.createChild();
43228          
43229         this.footer = Roo.factory(this.footer, Roo);
43230         
43231     }
43232     */
43233     
43234      if(typeof config == "string"){
43235         this.title = config;
43236     }else{
43237         Roo.apply(this, config);
43238     }
43239     
43240     if(this.resizeEl){
43241         this.resizeEl = Roo.get(this.resizeEl, true);
43242     }else{
43243         this.resizeEl = this.el;
43244     }
43245     // handle view.xtype
43246     
43247  
43248     
43249     
43250     this.addEvents({
43251         /**
43252          * @event activate
43253          * Fires when this panel is activated. 
43254          * @param {Roo.ContentPanel} this
43255          */
43256         "activate" : true,
43257         /**
43258          * @event deactivate
43259          * Fires when this panel is activated. 
43260          * @param {Roo.ContentPanel} this
43261          */
43262         "deactivate" : true,
43263
43264         /**
43265          * @event resize
43266          * Fires when this panel is resized if fitToFrame is true.
43267          * @param {Roo.ContentPanel} this
43268          * @param {Number} width The width after any component adjustments
43269          * @param {Number} height The height after any component adjustments
43270          */
43271         "resize" : true,
43272         
43273          /**
43274          * @event render
43275          * Fires when this tab is created
43276          * @param {Roo.ContentPanel} this
43277          */
43278         "render" : true,
43279         
43280           /**
43281          * @event scroll
43282          * Fires when this content is scrolled
43283          * @param {Roo.ContentPanel} this
43284          * @param {Event} scrollEvent
43285          */
43286         "scroll" : true
43287         
43288         
43289         
43290     });
43291     
43292
43293     
43294     
43295     if(this.autoScroll && !this.iframe){
43296         this.resizeEl.setStyle("overflow", "auto");
43297         this.resizeEl.on('scroll', this.onScroll, this);
43298     } else {
43299         // fix randome scrolling
43300         //this.el.on('scroll', function() {
43301         //    Roo.log('fix random scolling');
43302         //    this.scrollTo('top',0); 
43303         //});
43304     }
43305     content = content || this.content;
43306     if(content){
43307         this.setContent(content);
43308     }
43309     if(config && config.url){
43310         this.setUrl(this.url, this.params, this.loadOnce);
43311     }
43312     
43313     
43314     
43315     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43316     
43317     if (this.view && typeof(this.view.xtype) != 'undefined') {
43318         this.view.el = this.el.appendChild(document.createElement("div"));
43319         this.view = Roo.factory(this.view); 
43320         this.view.render  &&  this.view.render(false, '');  
43321     }
43322     
43323     
43324     this.fireEvent('render', this);
43325 };
43326
43327 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43328     
43329     cls : '',
43330     background : '',
43331     
43332     tabTip : '',
43333     
43334     iframe : false,
43335     iframeEl : false,
43336     
43337     /* Resize Element - use this to work out scroll etc. */
43338     resizeEl : false,
43339     
43340     setRegion : function(region){
43341         this.region = region;
43342         this.setActiveClass(region && !this.background);
43343     },
43344     
43345     
43346     setActiveClass: function(state)
43347     {
43348         if(state){
43349            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43350            this.el.setStyle('position','relative');
43351         }else{
43352            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43353            this.el.setStyle('position', 'absolute');
43354         } 
43355     },
43356     
43357     /**
43358      * Returns the toolbar for this Panel if one was configured. 
43359      * @return {Roo.Toolbar} 
43360      */
43361     getToolbar : function(){
43362         return this.toolbar;
43363     },
43364     
43365     setActiveState : function(active)
43366     {
43367         this.active = active;
43368         this.setActiveClass(active);
43369         if(!active){
43370             if(this.fireEvent("deactivate", this) === false){
43371                 return false;
43372             }
43373             return true;
43374         }
43375         this.fireEvent("activate", this);
43376         return true;
43377     },
43378     /**
43379      * Updates this panel's element (not for iframe)
43380      * @param {String} content The new content
43381      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43382     */
43383     setContent : function(content, loadScripts){
43384         if (this.iframe) {
43385             return;
43386         }
43387         
43388         this.el.update(content, loadScripts);
43389     },
43390
43391     ignoreResize : function(w, h)
43392     {
43393         //return false; // always resize?
43394         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43395             return true;
43396         }else{
43397             this.lastSize = {width: w, height: h};
43398             return false;
43399         }
43400     },
43401     /**
43402      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43403      * @return {Roo.UpdateManager} The UpdateManager
43404      */
43405     getUpdateManager : function(){
43406         if (this.iframe) {
43407             return false;
43408         }
43409         return this.el.getUpdateManager();
43410     },
43411      /**
43412      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43413      * Does not work with IFRAME contents
43414      * @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:
43415 <pre><code>
43416 panel.load({
43417     url: "your-url.php",
43418     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43419     callback: yourFunction,
43420     scope: yourObject, //(optional scope)
43421     discardUrl: false,
43422     nocache: false,
43423     text: "Loading...",
43424     timeout: 30,
43425     scripts: false
43426 });
43427 </code></pre>
43428      
43429      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43430      * 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.
43431      * @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}
43432      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43433      * @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.
43434      * @return {Roo.ContentPanel} this
43435      */
43436     load : function(){
43437         
43438         if (this.iframe) {
43439             return this;
43440         }
43441         
43442         var um = this.el.getUpdateManager();
43443         um.update.apply(um, arguments);
43444         return this;
43445     },
43446
43447
43448     /**
43449      * 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.
43450      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43451      * @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)
43452      * @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)
43453      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43454      */
43455     setUrl : function(url, params, loadOnce){
43456         if (this.iframe) {
43457             this.iframeEl.dom.src = url;
43458             return false;
43459         }
43460         
43461         if(this.refreshDelegate){
43462             this.removeListener("activate", this.refreshDelegate);
43463         }
43464         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43465         this.on("activate", this.refreshDelegate);
43466         return this.el.getUpdateManager();
43467     },
43468     
43469     _handleRefresh : function(url, params, loadOnce){
43470         if(!loadOnce || !this.loaded){
43471             var updater = this.el.getUpdateManager();
43472             updater.update(url, params, this._setLoaded.createDelegate(this));
43473         }
43474     },
43475     
43476     _setLoaded : function(){
43477         this.loaded = true;
43478     }, 
43479     
43480     /**
43481      * Returns this panel's id
43482      * @return {String} 
43483      */
43484     getId : function(){
43485         return this.el.id;
43486     },
43487     
43488     /** 
43489      * Returns this panel's element - used by regiosn to add.
43490      * @return {Roo.Element} 
43491      */
43492     getEl : function(){
43493         return this.wrapEl || this.el;
43494     },
43495     
43496    
43497     
43498     adjustForComponents : function(width, height)
43499     {
43500         //Roo.log('adjustForComponents ');
43501         if(this.resizeEl != this.el){
43502             width -= this.el.getFrameWidth('lr');
43503             height -= this.el.getFrameWidth('tb');
43504         }
43505         if(this.toolbar){
43506             var te = this.toolbar.getEl();
43507             te.setWidth(width);
43508             height -= te.getHeight();
43509         }
43510         if(this.footer){
43511             var te = this.footer.getEl();
43512             te.setWidth(width);
43513             height -= te.getHeight();
43514         }
43515         
43516         
43517         if(this.adjustments){
43518             width += this.adjustments[0];
43519             height += this.adjustments[1];
43520         }
43521         return {"width": width, "height": height};
43522     },
43523     
43524     setSize : function(width, height){
43525         if(this.fitToFrame && !this.ignoreResize(width, height)){
43526             if(this.fitContainer && this.resizeEl != this.el){
43527                 this.el.setSize(width, height);
43528             }
43529             var size = this.adjustForComponents(width, height);
43530             if (this.iframe) {
43531                 this.iframeEl.setSize(width,height);
43532             }
43533             
43534             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43535             this.fireEvent('resize', this, size.width, size.height);
43536             
43537             
43538         }
43539     },
43540     
43541     /**
43542      * Returns this panel's title
43543      * @return {String} 
43544      */
43545     getTitle : function(){
43546         
43547         if (typeof(this.title) != 'object') {
43548             return this.title;
43549         }
43550         
43551         var t = '';
43552         for (var k in this.title) {
43553             if (!this.title.hasOwnProperty(k)) {
43554                 continue;
43555             }
43556             
43557             if (k.indexOf('-') >= 0) {
43558                 var s = k.split('-');
43559                 for (var i = 0; i<s.length; i++) {
43560                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43561                 }
43562             } else {
43563                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43564             }
43565         }
43566         return t;
43567     },
43568     
43569     /**
43570      * Set this panel's title
43571      * @param {String} title
43572      */
43573     setTitle : function(title){
43574         this.title = title;
43575         if(this.region){
43576             this.region.updatePanelTitle(this, title);
43577         }
43578     },
43579     
43580     /**
43581      * Returns true is this panel was configured to be closable
43582      * @return {Boolean} 
43583      */
43584     isClosable : function(){
43585         return this.closable;
43586     },
43587     
43588     beforeSlide : function(){
43589         this.el.clip();
43590         this.resizeEl.clip();
43591     },
43592     
43593     afterSlide : function(){
43594         this.el.unclip();
43595         this.resizeEl.unclip();
43596     },
43597     
43598     /**
43599      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43600      *   Will fail silently if the {@link #setUrl} method has not been called.
43601      *   This does not activate the panel, just updates its content.
43602      */
43603     refresh : function(){
43604         if(this.refreshDelegate){
43605            this.loaded = false;
43606            this.refreshDelegate();
43607         }
43608     },
43609     
43610     /**
43611      * Destroys this panel
43612      */
43613     destroy : function(){
43614         this.el.removeAllListeners();
43615         var tempEl = document.createElement("span");
43616         tempEl.appendChild(this.el.dom);
43617         tempEl.innerHTML = "";
43618         this.el.remove();
43619         this.el = null;
43620     },
43621     
43622     /**
43623      * form - if the content panel contains a form - this is a reference to it.
43624      * @type {Roo.form.Form}
43625      */
43626     form : false,
43627     /**
43628      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43629      *    This contains a reference to it.
43630      * @type {Roo.View}
43631      */
43632     view : false,
43633     
43634       /**
43635      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43636      * <pre><code>
43637
43638 layout.addxtype({
43639        xtype : 'Form',
43640        items: [ .... ]
43641    }
43642 );
43643
43644 </code></pre>
43645      * @param {Object} cfg Xtype definition of item to add.
43646      */
43647     
43648     
43649     getChildContainer: function () {
43650         return this.getEl();
43651     },
43652     
43653     
43654     onScroll : function(e)
43655     {
43656         this.fireEvent('scroll', this, e);
43657     }
43658     
43659     
43660     /*
43661         var  ret = new Roo.factory(cfg);
43662         return ret;
43663         
43664         
43665         // add form..
43666         if (cfg.xtype.match(/^Form$/)) {
43667             
43668             var el;
43669             //if (this.footer) {
43670             //    el = this.footer.container.insertSibling(false, 'before');
43671             //} else {
43672                 el = this.el.createChild();
43673             //}
43674
43675             this.form = new  Roo.form.Form(cfg);
43676             
43677             
43678             if ( this.form.allItems.length) {
43679                 this.form.render(el.dom);
43680             }
43681             return this.form;
43682         }
43683         // should only have one of theses..
43684         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43685             // views.. should not be just added - used named prop 'view''
43686             
43687             cfg.el = this.el.appendChild(document.createElement("div"));
43688             // factory?
43689             
43690             var ret = new Roo.factory(cfg);
43691              
43692              ret.render && ret.render(false, ''); // render blank..
43693             this.view = ret;
43694             return ret;
43695         }
43696         return false;
43697     }
43698     \*/
43699 });
43700  
43701 /**
43702  * @class Roo.bootstrap.panel.Grid
43703  * @extends Roo.bootstrap.panel.Content
43704  * @constructor
43705  * Create a new GridPanel.
43706  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43707  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43708  * @param {Object} config A the config object
43709   
43710  */
43711
43712
43713
43714 Roo.bootstrap.panel.Grid = function(config)
43715 {
43716     
43717       
43718     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43719         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43720
43721     config.el = this.wrapper;
43722     //this.el = this.wrapper;
43723     
43724       if (config.container) {
43725         // ctor'ed from a Border/panel.grid
43726         
43727         
43728         this.wrapper.setStyle("overflow", "hidden");
43729         this.wrapper.addClass('roo-grid-container');
43730
43731     }
43732     
43733     
43734     if(config.toolbar){
43735         var tool_el = this.wrapper.createChild();    
43736         this.toolbar = Roo.factory(config.toolbar);
43737         var ti = [];
43738         if (config.toolbar.items) {
43739             ti = config.toolbar.items ;
43740             delete config.toolbar.items ;
43741         }
43742         
43743         var nitems = [];
43744         this.toolbar.render(tool_el);
43745         for(var i =0;i < ti.length;i++) {
43746           //  Roo.log(['add child', items[i]]);
43747             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43748         }
43749         this.toolbar.items = nitems;
43750         
43751         delete config.toolbar;
43752     }
43753     
43754     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43755     config.grid.scrollBody = true;;
43756     config.grid.monitorWindowResize = false; // turn off autosizing
43757     config.grid.autoHeight = false;
43758     config.grid.autoWidth = false;
43759     
43760     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43761     
43762     if (config.background) {
43763         // render grid on panel activation (if panel background)
43764         this.on('activate', function(gp) {
43765             if (!gp.grid.rendered) {
43766                 gp.grid.render(this.wrapper);
43767                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43768             }
43769         });
43770             
43771     } else {
43772         this.grid.render(this.wrapper);
43773         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43774
43775     }
43776     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43777     // ??? needed ??? config.el = this.wrapper;
43778     
43779     
43780     
43781   
43782     // xtype created footer. - not sure if will work as we normally have to render first..
43783     if (this.footer && !this.footer.el && this.footer.xtype) {
43784         
43785         var ctr = this.grid.getView().getFooterPanel(true);
43786         this.footer.dataSource = this.grid.dataSource;
43787         this.footer = Roo.factory(this.footer, Roo);
43788         this.footer.render(ctr);
43789         
43790     }
43791     
43792     
43793     
43794     
43795      
43796 };
43797
43798 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43799 {
43800   
43801     getId : function(){
43802         return this.grid.id;
43803     },
43804     
43805     /**
43806      * Returns the grid for this panel
43807      * @return {Roo.bootstrap.Table} 
43808      */
43809     getGrid : function(){
43810         return this.grid;    
43811     },
43812     
43813     setSize : function(width, height)
43814     {
43815      
43816         //if(!this.ignoreResize(width, height)){
43817             var grid = this.grid;
43818             var size = this.adjustForComponents(width, height);
43819             // tfoot is not a footer?
43820           
43821             
43822             var gridel = grid.getGridEl();
43823             gridel.setSize(size.width, size.height);
43824             
43825             var tbd = grid.getGridEl().select('tbody', true).first();
43826             var thd = grid.getGridEl().select('thead',true).first();
43827             var tbf= grid.getGridEl().select('tfoot', true).first();
43828
43829             if (tbf) {
43830                 size.height -= tbf.getHeight();
43831             }
43832             if (thd) {
43833                 size.height -= thd.getHeight();
43834             }
43835             
43836             tbd.setSize(size.width, size.height );
43837             // this is for the account management tab -seems to work there.
43838             var thd = grid.getGridEl().select('thead',true).first();
43839             //if (tbd) {
43840             //    tbd.setSize(size.width, size.height - thd.getHeight());
43841             //}
43842              
43843             grid.autoSize();
43844         //}
43845    
43846     },
43847      
43848     
43849     
43850     beforeSlide : function(){
43851         this.grid.getView().scroller.clip();
43852     },
43853     
43854     afterSlide : function(){
43855         this.grid.getView().scroller.unclip();
43856     },
43857     
43858     destroy : function(){
43859         this.grid.destroy();
43860         delete this.grid;
43861         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43862     }
43863 });
43864
43865 /**
43866  * @class Roo.bootstrap.panel.Nest
43867  * @extends Roo.bootstrap.panel.Content
43868  * @constructor
43869  * Create a new Panel, that can contain a layout.Border.
43870  * 
43871  * 
43872  * @param {String/Object} config A string to set only the title or a config object
43873  */
43874 Roo.bootstrap.panel.Nest = function(config)
43875 {
43876     // construct with only one argument..
43877     /* FIXME - implement nicer consturctors
43878     if (layout.layout) {
43879         config = layout;
43880         layout = config.layout;
43881         delete config.layout;
43882     }
43883     if (layout.xtype && !layout.getEl) {
43884         // then layout needs constructing..
43885         layout = Roo.factory(layout, Roo);
43886     }
43887     */
43888     
43889     config.el =  config.layout.getEl();
43890     
43891     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43892     
43893     config.layout.monitorWindowResize = false; // turn off autosizing
43894     this.layout = config.layout;
43895     this.layout.getEl().addClass("roo-layout-nested-layout");
43896     this.layout.parent = this;
43897     
43898     
43899     
43900     
43901 };
43902
43903 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43904     /**
43905     * @cfg {Roo.BorderLayout} layout The layout for this panel
43906     */
43907     layout : false,
43908
43909     setSize : function(width, height){
43910         if(!this.ignoreResize(width, height)){
43911             var size = this.adjustForComponents(width, height);
43912             var el = this.layout.getEl();
43913             if (size.height < 1) {
43914                 el.setWidth(size.width);   
43915             } else {
43916                 el.setSize(size.width, size.height);
43917             }
43918             var touch = el.dom.offsetWidth;
43919             this.layout.layout();
43920             // ie requires a double layout on the first pass
43921             if(Roo.isIE && !this.initialized){
43922                 this.initialized = true;
43923                 this.layout.layout();
43924             }
43925         }
43926     },
43927     
43928     // activate all subpanels if not currently active..
43929     
43930     setActiveState : function(active){
43931         this.active = active;
43932         this.setActiveClass(active);
43933         
43934         if(!active){
43935             this.fireEvent("deactivate", this);
43936             return;
43937         }
43938         
43939         this.fireEvent("activate", this);
43940         // not sure if this should happen before or after..
43941         if (!this.layout) {
43942             return; // should not happen..
43943         }
43944         var reg = false;
43945         for (var r in this.layout.regions) {
43946             reg = this.layout.getRegion(r);
43947             if (reg.getActivePanel()) {
43948                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43949                 reg.setActivePanel(reg.getActivePanel());
43950                 continue;
43951             }
43952             if (!reg.panels.length) {
43953                 continue;
43954             }
43955             reg.showPanel(reg.getPanel(0));
43956         }
43957         
43958         
43959         
43960         
43961     },
43962     
43963     /**
43964      * Returns the nested BorderLayout for this panel
43965      * @return {Roo.BorderLayout} 
43966      */
43967     getLayout : function(){
43968         return this.layout;
43969     },
43970     
43971      /**
43972      * Adds a xtype elements to the layout of the nested panel
43973      * <pre><code>
43974
43975 panel.addxtype({
43976        xtype : 'ContentPanel',
43977        region: 'west',
43978        items: [ .... ]
43979    }
43980 );
43981
43982 panel.addxtype({
43983         xtype : 'NestedLayoutPanel',
43984         region: 'west',
43985         layout: {
43986            center: { },
43987            west: { }   
43988         },
43989         items : [ ... list of content panels or nested layout panels.. ]
43990    }
43991 );
43992 </code></pre>
43993      * @param {Object} cfg Xtype definition of item to add.
43994      */
43995     addxtype : function(cfg) {
43996         return this.layout.addxtype(cfg);
43997     
43998     }
43999 });/*
44000  * Based on:
44001  * Ext JS Library 1.1.1
44002  * Copyright(c) 2006-2007, Ext JS, LLC.
44003  *
44004  * Originally Released Under LGPL - original licence link has changed is not relivant.
44005  *
44006  * Fork - LGPL
44007  * <script type="text/javascript">
44008  */
44009 /**
44010  * @class Roo.TabPanel
44011  * @extends Roo.util.Observable
44012  * A lightweight tab container.
44013  * <br><br>
44014  * Usage:
44015  * <pre><code>
44016 // basic tabs 1, built from existing content
44017 var tabs = new Roo.TabPanel("tabs1");
44018 tabs.addTab("script", "View Script");
44019 tabs.addTab("markup", "View Markup");
44020 tabs.activate("script");
44021
44022 // more advanced tabs, built from javascript
44023 var jtabs = new Roo.TabPanel("jtabs");
44024 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44025
44026 // set up the UpdateManager
44027 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44028 var updater = tab2.getUpdateManager();
44029 updater.setDefaultUrl("ajax1.htm");
44030 tab2.on('activate', updater.refresh, updater, true);
44031
44032 // Use setUrl for Ajax loading
44033 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44034 tab3.setUrl("ajax2.htm", null, true);
44035
44036 // Disabled tab
44037 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44038 tab4.disable();
44039
44040 jtabs.activate("jtabs-1");
44041  * </code></pre>
44042  * @constructor
44043  * Create a new TabPanel.
44044  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44045  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44046  */
44047 Roo.bootstrap.panel.Tabs = function(config){
44048     /**
44049     * The container element for this TabPanel.
44050     * @type Roo.Element
44051     */
44052     this.el = Roo.get(config.el);
44053     delete config.el;
44054     if(config){
44055         if(typeof config == "boolean"){
44056             this.tabPosition = config ? "bottom" : "top";
44057         }else{
44058             Roo.apply(this, config);
44059         }
44060     }
44061     
44062     if(this.tabPosition == "bottom"){
44063         // if tabs are at the bottom = create the body first.
44064         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44065         this.el.addClass("roo-tabs-bottom");
44066     }
44067     // next create the tabs holders
44068     
44069     if (this.tabPosition == "west"){
44070         
44071         var reg = this.region; // fake it..
44072         while (reg) {
44073             if (!reg.mgr.parent) {
44074                 break;
44075             }
44076             reg = reg.mgr.parent.region;
44077         }
44078         Roo.log("got nest?");
44079         Roo.log(reg);
44080         if (reg.mgr.getRegion('west')) {
44081             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44082             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44083             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44084             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44085             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44086         
44087             
44088         }
44089         
44090         
44091     } else {
44092      
44093         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44094         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44095         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44096         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44097     }
44098     
44099     
44100     if(Roo.isIE){
44101         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44102     }
44103     
44104     // finally - if tabs are at the top, then create the body last..
44105     if(this.tabPosition != "bottom"){
44106         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44107          * @type Roo.Element
44108          */
44109         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44110         this.el.addClass("roo-tabs-top");
44111     }
44112     this.items = [];
44113
44114     this.bodyEl.setStyle("position", "relative");
44115
44116     this.active = null;
44117     this.activateDelegate = this.activate.createDelegate(this);
44118
44119     this.addEvents({
44120         /**
44121          * @event tabchange
44122          * Fires when the active tab changes
44123          * @param {Roo.TabPanel} this
44124          * @param {Roo.TabPanelItem} activePanel The new active tab
44125          */
44126         "tabchange": true,
44127         /**
44128          * @event beforetabchange
44129          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44130          * @param {Roo.TabPanel} this
44131          * @param {Object} e Set cancel to true on this object to cancel the tab change
44132          * @param {Roo.TabPanelItem} tab The tab being changed to
44133          */
44134         "beforetabchange" : true
44135     });
44136
44137     Roo.EventManager.onWindowResize(this.onResize, this);
44138     this.cpad = this.el.getPadding("lr");
44139     this.hiddenCount = 0;
44140
44141
44142     // toolbar on the tabbar support...
44143     if (this.toolbar) {
44144         alert("no toolbar support yet");
44145         this.toolbar  = false;
44146         /*
44147         var tcfg = this.toolbar;
44148         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44149         this.toolbar = new Roo.Toolbar(tcfg);
44150         if (Roo.isSafari) {
44151             var tbl = tcfg.container.child('table', true);
44152             tbl.setAttribute('width', '100%');
44153         }
44154         */
44155         
44156     }
44157    
44158
44159
44160     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44161 };
44162
44163 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44164     /*
44165      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44166      */
44167     tabPosition : "top",
44168     /*
44169      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44170      */
44171     currentTabWidth : 0,
44172     /*
44173      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44174      */
44175     minTabWidth : 40,
44176     /*
44177      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44178      */
44179     maxTabWidth : 250,
44180     /*
44181      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44182      */
44183     preferredTabWidth : 175,
44184     /*
44185      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44186      */
44187     resizeTabs : false,
44188     /*
44189      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44190      */
44191     monitorResize : true,
44192     /*
44193      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44194      */
44195     toolbar : false,  // set by caller..
44196     
44197     region : false, /// set by caller
44198     
44199     disableTooltips : true, // not used yet...
44200
44201     /**
44202      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44203      * @param {String} id The id of the div to use <b>or create</b>
44204      * @param {String} text The text for the tab
44205      * @param {String} content (optional) Content to put in the TabPanelItem body
44206      * @param {Boolean} closable (optional) True to create a close icon on the tab
44207      * @return {Roo.TabPanelItem} The created TabPanelItem
44208      */
44209     addTab : function(id, text, content, closable, tpl)
44210     {
44211         var item = new Roo.bootstrap.panel.TabItem({
44212             panel: this,
44213             id : id,
44214             text : text,
44215             closable : closable,
44216             tpl : tpl
44217         });
44218         this.addTabItem(item);
44219         if(content){
44220             item.setContent(content);
44221         }
44222         return item;
44223     },
44224
44225     /**
44226      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44227      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44228      * @return {Roo.TabPanelItem}
44229      */
44230     getTab : function(id){
44231         return this.items[id];
44232     },
44233
44234     /**
44235      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44236      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44237      */
44238     hideTab : function(id){
44239         var t = this.items[id];
44240         if(!t.isHidden()){
44241            t.setHidden(true);
44242            this.hiddenCount++;
44243            this.autoSizeTabs();
44244         }
44245     },
44246
44247     /**
44248      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44249      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44250      */
44251     unhideTab : function(id){
44252         var t = this.items[id];
44253         if(t.isHidden()){
44254            t.setHidden(false);
44255            this.hiddenCount--;
44256            this.autoSizeTabs();
44257         }
44258     },
44259
44260     /**
44261      * Adds an existing {@link Roo.TabPanelItem}.
44262      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44263      */
44264     addTabItem : function(item)
44265     {
44266         this.items[item.id] = item;
44267         this.items.push(item);
44268         this.autoSizeTabs();
44269       //  if(this.resizeTabs){
44270     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44271   //         this.autoSizeTabs();
44272 //        }else{
44273 //            item.autoSize();
44274        // }
44275     },
44276
44277     /**
44278      * Removes a {@link Roo.TabPanelItem}.
44279      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44280      */
44281     removeTab : function(id){
44282         var items = this.items;
44283         var tab = items[id];
44284         if(!tab) { return; }
44285         var index = items.indexOf(tab);
44286         if(this.active == tab && items.length > 1){
44287             var newTab = this.getNextAvailable(index);
44288             if(newTab) {
44289                 newTab.activate();
44290             }
44291         }
44292         this.stripEl.dom.removeChild(tab.pnode.dom);
44293         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44294             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44295         }
44296         items.splice(index, 1);
44297         delete this.items[tab.id];
44298         tab.fireEvent("close", tab);
44299         tab.purgeListeners();
44300         this.autoSizeTabs();
44301     },
44302
44303     getNextAvailable : function(start){
44304         var items = this.items;
44305         var index = start;
44306         // look for a next tab that will slide over to
44307         // replace the one being removed
44308         while(index < items.length){
44309             var item = items[++index];
44310             if(item && !item.isHidden()){
44311                 return item;
44312             }
44313         }
44314         // if one isn't found select the previous tab (on the left)
44315         index = start;
44316         while(index >= 0){
44317             var item = items[--index];
44318             if(item && !item.isHidden()){
44319                 return item;
44320             }
44321         }
44322         return null;
44323     },
44324
44325     /**
44326      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44327      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44328      */
44329     disableTab : function(id){
44330         var tab = this.items[id];
44331         if(tab && this.active != tab){
44332             tab.disable();
44333         }
44334     },
44335
44336     /**
44337      * Enables a {@link Roo.TabPanelItem} that is disabled.
44338      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44339      */
44340     enableTab : function(id){
44341         var tab = this.items[id];
44342         tab.enable();
44343     },
44344
44345     /**
44346      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44347      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44348      * @return {Roo.TabPanelItem} The TabPanelItem.
44349      */
44350     activate : function(id)
44351     {
44352         //Roo.log('activite:'  + id);
44353         
44354         var tab = this.items[id];
44355         if(!tab){
44356             return null;
44357         }
44358         if(tab == this.active || tab.disabled){
44359             return tab;
44360         }
44361         var e = {};
44362         this.fireEvent("beforetabchange", this, e, tab);
44363         if(e.cancel !== true && !tab.disabled){
44364             if(this.active){
44365                 this.active.hide();
44366             }
44367             this.active = this.items[id];
44368             this.active.show();
44369             this.fireEvent("tabchange", this, this.active);
44370         }
44371         return tab;
44372     },
44373
44374     /**
44375      * Gets the active {@link Roo.TabPanelItem}.
44376      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44377      */
44378     getActiveTab : function(){
44379         return this.active;
44380     },
44381
44382     /**
44383      * Updates the tab body element to fit the height of the container element
44384      * for overflow scrolling
44385      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44386      */
44387     syncHeight : function(targetHeight){
44388         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44389         var bm = this.bodyEl.getMargins();
44390         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44391         this.bodyEl.setHeight(newHeight);
44392         return newHeight;
44393     },
44394
44395     onResize : function(){
44396         if(this.monitorResize){
44397             this.autoSizeTabs();
44398         }
44399     },
44400
44401     /**
44402      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44403      */
44404     beginUpdate : function(){
44405         this.updating = true;
44406     },
44407
44408     /**
44409      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44410      */
44411     endUpdate : function(){
44412         this.updating = false;
44413         this.autoSizeTabs();
44414     },
44415
44416     /**
44417      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44418      */
44419     autoSizeTabs : function()
44420     {
44421         var count = this.items.length;
44422         var vcount = count - this.hiddenCount;
44423         
44424         if (vcount < 2) {
44425             this.stripEl.hide();
44426         } else {
44427             this.stripEl.show();
44428         }
44429         
44430         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44431             return;
44432         }
44433         
44434         
44435         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44436         var availWidth = Math.floor(w / vcount);
44437         var b = this.stripBody;
44438         if(b.getWidth() > w){
44439             var tabs = this.items;
44440             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44441             if(availWidth < this.minTabWidth){
44442                 /*if(!this.sleft){    // incomplete scrolling code
44443                     this.createScrollButtons();
44444                 }
44445                 this.showScroll();
44446                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44447             }
44448         }else{
44449             if(this.currentTabWidth < this.preferredTabWidth){
44450                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44451             }
44452         }
44453     },
44454
44455     /**
44456      * Returns the number of tabs in this TabPanel.
44457      * @return {Number}
44458      */
44459      getCount : function(){
44460          return this.items.length;
44461      },
44462
44463     /**
44464      * Resizes all the tabs to the passed width
44465      * @param {Number} The new width
44466      */
44467     setTabWidth : function(width){
44468         this.currentTabWidth = width;
44469         for(var i = 0, len = this.items.length; i < len; i++) {
44470                 if(!this.items[i].isHidden()) {
44471                 this.items[i].setWidth(width);
44472             }
44473         }
44474     },
44475
44476     /**
44477      * Destroys this TabPanel
44478      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44479      */
44480     destroy : function(removeEl){
44481         Roo.EventManager.removeResizeListener(this.onResize, this);
44482         for(var i = 0, len = this.items.length; i < len; i++){
44483             this.items[i].purgeListeners();
44484         }
44485         if(removeEl === true){
44486             this.el.update("");
44487             this.el.remove();
44488         }
44489     },
44490     
44491     createStrip : function(container)
44492     {
44493         var strip = document.createElement("nav");
44494         strip.className = Roo.bootstrap.version == 4 ?
44495             "navbar-light bg-light" : 
44496             "navbar navbar-default"; //"x-tabs-wrap";
44497         container.appendChild(strip);
44498         return strip;
44499     },
44500     
44501     createStripList : function(strip)
44502     {
44503         // div wrapper for retard IE
44504         // returns the "tr" element.
44505         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44506         //'<div class="x-tabs-strip-wrap">'+
44507           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44508           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44509         return strip.firstChild; //.firstChild.firstChild.firstChild;
44510     },
44511     createBody : function(container)
44512     {
44513         var body = document.createElement("div");
44514         Roo.id(body, "tab-body");
44515         //Roo.fly(body).addClass("x-tabs-body");
44516         Roo.fly(body).addClass("tab-content");
44517         container.appendChild(body);
44518         return body;
44519     },
44520     createItemBody :function(bodyEl, id){
44521         var body = Roo.getDom(id);
44522         if(!body){
44523             body = document.createElement("div");
44524             body.id = id;
44525         }
44526         //Roo.fly(body).addClass("x-tabs-item-body");
44527         Roo.fly(body).addClass("tab-pane");
44528          bodyEl.insertBefore(body, bodyEl.firstChild);
44529         return body;
44530     },
44531     /** @private */
44532     createStripElements :  function(stripEl, text, closable, tpl)
44533     {
44534         var td = document.createElement("li"); // was td..
44535         td.className = 'nav-item';
44536         
44537         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44538         
44539         
44540         stripEl.appendChild(td);
44541         /*if(closable){
44542             td.className = "x-tabs-closable";
44543             if(!this.closeTpl){
44544                 this.closeTpl = new Roo.Template(
44545                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44546                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44547                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44548                 );
44549             }
44550             var el = this.closeTpl.overwrite(td, {"text": text});
44551             var close = el.getElementsByTagName("div")[0];
44552             var inner = el.getElementsByTagName("em")[0];
44553             return {"el": el, "close": close, "inner": inner};
44554         } else {
44555         */
44556         // not sure what this is..
44557 //            if(!this.tabTpl){
44558                 //this.tabTpl = new Roo.Template(
44559                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44560                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44561                 //);
44562 //                this.tabTpl = new Roo.Template(
44563 //                   '<a href="#">' +
44564 //                   '<span unselectable="on"' +
44565 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44566 //                            ' >{text}</span></a>'
44567 //                );
44568 //                
44569 //            }
44570
44571
44572             var template = tpl || this.tabTpl || false;
44573             
44574             if(!template){
44575                 template =  new Roo.Template(
44576                         Roo.bootstrap.version == 4 ? 
44577                             (
44578                                 '<a class="nav-link" href="#" unselectable="on"' +
44579                                      (this.disableTooltips ? '' : ' title="{text}"') +
44580                                      ' >{text}</a>'
44581                             ) : (
44582                                 '<a class="nav-link" href="#">' +
44583                                 '<span unselectable="on"' +
44584                                          (this.disableTooltips ? '' : ' title="{text}"') +
44585                                     ' >{text}</span></a>'
44586                             )
44587                 );
44588             }
44589             
44590             switch (typeof(template)) {
44591                 case 'object' :
44592                     break;
44593                 case 'string' :
44594                     template = new Roo.Template(template);
44595                     break;
44596                 default :
44597                     break;
44598             }
44599             
44600             var el = template.overwrite(td, {"text": text});
44601             
44602             var inner = el.getElementsByTagName("span")[0];
44603             
44604             return {"el": el, "inner": inner};
44605             
44606     }
44607         
44608     
44609 });
44610
44611 /**
44612  * @class Roo.TabPanelItem
44613  * @extends Roo.util.Observable
44614  * Represents an individual item (tab plus body) in a TabPanel.
44615  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44616  * @param {String} id The id of this TabPanelItem
44617  * @param {String} text The text for the tab of this TabPanelItem
44618  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44619  */
44620 Roo.bootstrap.panel.TabItem = function(config){
44621     /**
44622      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44623      * @type Roo.TabPanel
44624      */
44625     this.tabPanel = config.panel;
44626     /**
44627      * The id for this TabPanelItem
44628      * @type String
44629      */
44630     this.id = config.id;
44631     /** @private */
44632     this.disabled = false;
44633     /** @private */
44634     this.text = config.text;
44635     /** @private */
44636     this.loaded = false;
44637     this.closable = config.closable;
44638
44639     /**
44640      * The body element for this TabPanelItem.
44641      * @type Roo.Element
44642      */
44643     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44644     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44645     this.bodyEl.setStyle("display", "block");
44646     this.bodyEl.setStyle("zoom", "1");
44647     //this.hideAction();
44648
44649     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44650     /** @private */
44651     this.el = Roo.get(els.el);
44652     this.inner = Roo.get(els.inner, true);
44653      this.textEl = Roo.bootstrap.version == 4 ?
44654         this.el : Roo.get(this.el.dom.firstChild, true);
44655
44656     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44657     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44658
44659     
44660 //    this.el.on("mousedown", this.onTabMouseDown, this);
44661     this.el.on("click", this.onTabClick, this);
44662     /** @private */
44663     if(config.closable){
44664         var c = Roo.get(els.close, true);
44665         c.dom.title = this.closeText;
44666         c.addClassOnOver("close-over");
44667         c.on("click", this.closeClick, this);
44668      }
44669
44670     this.addEvents({
44671          /**
44672          * @event activate
44673          * Fires when this tab becomes the active tab.
44674          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44675          * @param {Roo.TabPanelItem} this
44676          */
44677         "activate": true,
44678         /**
44679          * @event beforeclose
44680          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44681          * @param {Roo.TabPanelItem} this
44682          * @param {Object} e Set cancel to true on this object to cancel the close.
44683          */
44684         "beforeclose": true,
44685         /**
44686          * @event close
44687          * Fires when this tab is closed.
44688          * @param {Roo.TabPanelItem} this
44689          */
44690          "close": true,
44691         /**
44692          * @event deactivate
44693          * Fires when this tab is no longer the active tab.
44694          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44695          * @param {Roo.TabPanelItem} this
44696          */
44697          "deactivate" : true
44698     });
44699     this.hidden = false;
44700
44701     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44702 };
44703
44704 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44705            {
44706     purgeListeners : function(){
44707        Roo.util.Observable.prototype.purgeListeners.call(this);
44708        this.el.removeAllListeners();
44709     },
44710     /**
44711      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44712      */
44713     show : function(){
44714         this.status_node.addClass("active");
44715         this.showAction();
44716         if(Roo.isOpera){
44717             this.tabPanel.stripWrap.repaint();
44718         }
44719         this.fireEvent("activate", this.tabPanel, this);
44720     },
44721
44722     /**
44723      * Returns true if this tab is the active tab.
44724      * @return {Boolean}
44725      */
44726     isActive : function(){
44727         return this.tabPanel.getActiveTab() == this;
44728     },
44729
44730     /**
44731      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44732      */
44733     hide : function(){
44734         this.status_node.removeClass("active");
44735         this.hideAction();
44736         this.fireEvent("deactivate", this.tabPanel, this);
44737     },
44738
44739     hideAction : function(){
44740         this.bodyEl.hide();
44741         this.bodyEl.setStyle("position", "absolute");
44742         this.bodyEl.setLeft("-20000px");
44743         this.bodyEl.setTop("-20000px");
44744     },
44745
44746     showAction : function(){
44747         this.bodyEl.setStyle("position", "relative");
44748         this.bodyEl.setTop("");
44749         this.bodyEl.setLeft("");
44750         this.bodyEl.show();
44751     },
44752
44753     /**
44754      * Set the tooltip for the tab.
44755      * @param {String} tooltip The tab's tooltip
44756      */
44757     setTooltip : function(text){
44758         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44759             this.textEl.dom.qtip = text;
44760             this.textEl.dom.removeAttribute('title');
44761         }else{
44762             this.textEl.dom.title = text;
44763         }
44764     },
44765
44766     onTabClick : function(e){
44767         e.preventDefault();
44768         this.tabPanel.activate(this.id);
44769     },
44770
44771     onTabMouseDown : function(e){
44772         e.preventDefault();
44773         this.tabPanel.activate(this.id);
44774     },
44775 /*
44776     getWidth : function(){
44777         return this.inner.getWidth();
44778     },
44779
44780     setWidth : function(width){
44781         var iwidth = width - this.linode.getPadding("lr");
44782         this.inner.setWidth(iwidth);
44783         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44784         this.linode.setWidth(width);
44785     },
44786 */
44787     /**
44788      * Show or hide the tab
44789      * @param {Boolean} hidden True to hide or false to show.
44790      */
44791     setHidden : function(hidden){
44792         this.hidden = hidden;
44793         this.linode.setStyle("display", hidden ? "none" : "");
44794     },
44795
44796     /**
44797      * Returns true if this tab is "hidden"
44798      * @return {Boolean}
44799      */
44800     isHidden : function(){
44801         return this.hidden;
44802     },
44803
44804     /**
44805      * Returns the text for this tab
44806      * @return {String}
44807      */
44808     getText : function(){
44809         return this.text;
44810     },
44811     /*
44812     autoSize : function(){
44813         //this.el.beginMeasure();
44814         this.textEl.setWidth(1);
44815         /*
44816          *  #2804 [new] Tabs in Roojs
44817          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44818          */
44819         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44820         //this.el.endMeasure();
44821     //},
44822
44823     /**
44824      * Sets the text for the tab (Note: this also sets the tooltip text)
44825      * @param {String} text The tab's text and tooltip
44826      */
44827     setText : function(text){
44828         this.text = text;
44829         this.textEl.update(text);
44830         this.setTooltip(text);
44831         //if(!this.tabPanel.resizeTabs){
44832         //    this.autoSize();
44833         //}
44834     },
44835     /**
44836      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44837      */
44838     activate : function(){
44839         this.tabPanel.activate(this.id);
44840     },
44841
44842     /**
44843      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44844      */
44845     disable : function(){
44846         if(this.tabPanel.active != this){
44847             this.disabled = true;
44848             this.status_node.addClass("disabled");
44849         }
44850     },
44851
44852     /**
44853      * Enables this TabPanelItem if it was previously disabled.
44854      */
44855     enable : function(){
44856         this.disabled = false;
44857         this.status_node.removeClass("disabled");
44858     },
44859
44860     /**
44861      * Sets the content for this TabPanelItem.
44862      * @param {String} content The content
44863      * @param {Boolean} loadScripts true to look for and load scripts
44864      */
44865     setContent : function(content, loadScripts){
44866         this.bodyEl.update(content, loadScripts);
44867     },
44868
44869     /**
44870      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44871      * @return {Roo.UpdateManager} The UpdateManager
44872      */
44873     getUpdateManager : function(){
44874         return this.bodyEl.getUpdateManager();
44875     },
44876
44877     /**
44878      * Set a URL to be used to load the content for this TabPanelItem.
44879      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44880      * @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)
44881      * @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)
44882      * @return {Roo.UpdateManager} The UpdateManager
44883      */
44884     setUrl : function(url, params, loadOnce){
44885         if(this.refreshDelegate){
44886             this.un('activate', this.refreshDelegate);
44887         }
44888         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44889         this.on("activate", this.refreshDelegate);
44890         return this.bodyEl.getUpdateManager();
44891     },
44892
44893     /** @private */
44894     _handleRefresh : function(url, params, loadOnce){
44895         if(!loadOnce || !this.loaded){
44896             var updater = this.bodyEl.getUpdateManager();
44897             updater.update(url, params, this._setLoaded.createDelegate(this));
44898         }
44899     },
44900
44901     /**
44902      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44903      *   Will fail silently if the setUrl method has not been called.
44904      *   This does not activate the panel, just updates its content.
44905      */
44906     refresh : function(){
44907         if(this.refreshDelegate){
44908            this.loaded = false;
44909            this.refreshDelegate();
44910         }
44911     },
44912
44913     /** @private */
44914     _setLoaded : function(){
44915         this.loaded = true;
44916     },
44917
44918     /** @private */
44919     closeClick : function(e){
44920         var o = {};
44921         e.stopEvent();
44922         this.fireEvent("beforeclose", this, o);
44923         if(o.cancel !== true){
44924             this.tabPanel.removeTab(this.id);
44925         }
44926     },
44927     /**
44928      * The text displayed in the tooltip for the close icon.
44929      * @type String
44930      */
44931     closeText : "Close this tab"
44932 });
44933 /**
44934 *    This script refer to:
44935 *    Title: International Telephone Input
44936 *    Author: Jack O'Connor
44937 *    Code version:  v12.1.12
44938 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44939 **/
44940
44941 Roo.bootstrap.form.PhoneInputData = function() {
44942     var d = [
44943       [
44944         "Afghanistan (‫افغانستان‬‎)",
44945         "af",
44946         "93"
44947       ],
44948       [
44949         "Albania (Shqipëri)",
44950         "al",
44951         "355"
44952       ],
44953       [
44954         "Algeria (‫الجزائر‬‎)",
44955         "dz",
44956         "213"
44957       ],
44958       [
44959         "American Samoa",
44960         "as",
44961         "1684"
44962       ],
44963       [
44964         "Andorra",
44965         "ad",
44966         "376"
44967       ],
44968       [
44969         "Angola",
44970         "ao",
44971         "244"
44972       ],
44973       [
44974         "Anguilla",
44975         "ai",
44976         "1264"
44977       ],
44978       [
44979         "Antigua and Barbuda",
44980         "ag",
44981         "1268"
44982       ],
44983       [
44984         "Argentina",
44985         "ar",
44986         "54"
44987       ],
44988       [
44989         "Armenia (Հայաստան)",
44990         "am",
44991         "374"
44992       ],
44993       [
44994         "Aruba",
44995         "aw",
44996         "297"
44997       ],
44998       [
44999         "Australia",
45000         "au",
45001         "61",
45002         0
45003       ],
45004       [
45005         "Austria (Österreich)",
45006         "at",
45007         "43"
45008       ],
45009       [
45010         "Azerbaijan (Azərbaycan)",
45011         "az",
45012         "994"
45013       ],
45014       [
45015         "Bahamas",
45016         "bs",
45017         "1242"
45018       ],
45019       [
45020         "Bahrain (‫البحرين‬‎)",
45021         "bh",
45022         "973"
45023       ],
45024       [
45025         "Bangladesh (বাংলাদেশ)",
45026         "bd",
45027         "880"
45028       ],
45029       [
45030         "Barbados",
45031         "bb",
45032         "1246"
45033       ],
45034       [
45035         "Belarus (Беларусь)",
45036         "by",
45037         "375"
45038       ],
45039       [
45040         "Belgium (België)",
45041         "be",
45042         "32"
45043       ],
45044       [
45045         "Belize",
45046         "bz",
45047         "501"
45048       ],
45049       [
45050         "Benin (Bénin)",
45051         "bj",
45052         "229"
45053       ],
45054       [
45055         "Bermuda",
45056         "bm",
45057         "1441"
45058       ],
45059       [
45060         "Bhutan (འབྲུག)",
45061         "bt",
45062         "975"
45063       ],
45064       [
45065         "Bolivia",
45066         "bo",
45067         "591"
45068       ],
45069       [
45070         "Bosnia and Herzegovina (Босна и Херцеговина)",
45071         "ba",
45072         "387"
45073       ],
45074       [
45075         "Botswana",
45076         "bw",
45077         "267"
45078       ],
45079       [
45080         "Brazil (Brasil)",
45081         "br",
45082         "55"
45083       ],
45084       [
45085         "British Indian Ocean Territory",
45086         "io",
45087         "246"
45088       ],
45089       [
45090         "British Virgin Islands",
45091         "vg",
45092         "1284"
45093       ],
45094       [
45095         "Brunei",
45096         "bn",
45097         "673"
45098       ],
45099       [
45100         "Bulgaria (България)",
45101         "bg",
45102         "359"
45103       ],
45104       [
45105         "Burkina Faso",
45106         "bf",
45107         "226"
45108       ],
45109       [
45110         "Burundi (Uburundi)",
45111         "bi",
45112         "257"
45113       ],
45114       [
45115         "Cambodia (កម្ពុជា)",
45116         "kh",
45117         "855"
45118       ],
45119       [
45120         "Cameroon (Cameroun)",
45121         "cm",
45122         "237"
45123       ],
45124       [
45125         "Canada",
45126         "ca",
45127         "1",
45128         1,
45129         ["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"]
45130       ],
45131       [
45132         "Cape Verde (Kabu Verdi)",
45133         "cv",
45134         "238"
45135       ],
45136       [
45137         "Caribbean Netherlands",
45138         "bq",
45139         "599",
45140         1
45141       ],
45142       [
45143         "Cayman Islands",
45144         "ky",
45145         "1345"
45146       ],
45147       [
45148         "Central African Republic (République centrafricaine)",
45149         "cf",
45150         "236"
45151       ],
45152       [
45153         "Chad (Tchad)",
45154         "td",
45155         "235"
45156       ],
45157       [
45158         "Chile",
45159         "cl",
45160         "56"
45161       ],
45162       [
45163         "China (中国)",
45164         "cn",
45165         "86"
45166       ],
45167       [
45168         "Christmas Island",
45169         "cx",
45170         "61",
45171         2
45172       ],
45173       [
45174         "Cocos (Keeling) Islands",
45175         "cc",
45176         "61",
45177         1
45178       ],
45179       [
45180         "Colombia",
45181         "co",
45182         "57"
45183       ],
45184       [
45185         "Comoros (‫جزر القمر‬‎)",
45186         "km",
45187         "269"
45188       ],
45189       [
45190         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45191         "cd",
45192         "243"
45193       ],
45194       [
45195         "Congo (Republic) (Congo-Brazzaville)",
45196         "cg",
45197         "242"
45198       ],
45199       [
45200         "Cook Islands",
45201         "ck",
45202         "682"
45203       ],
45204       [
45205         "Costa Rica",
45206         "cr",
45207         "506"
45208       ],
45209       [
45210         "Côte d’Ivoire",
45211         "ci",
45212         "225"
45213       ],
45214       [
45215         "Croatia (Hrvatska)",
45216         "hr",
45217         "385"
45218       ],
45219       [
45220         "Cuba",
45221         "cu",
45222         "53"
45223       ],
45224       [
45225         "Curaçao",
45226         "cw",
45227         "599",
45228         0
45229       ],
45230       [
45231         "Cyprus (Κύπρος)",
45232         "cy",
45233         "357"
45234       ],
45235       [
45236         "Czech Republic (Česká republika)",
45237         "cz",
45238         "420"
45239       ],
45240       [
45241         "Denmark (Danmark)",
45242         "dk",
45243         "45"
45244       ],
45245       [
45246         "Djibouti",
45247         "dj",
45248         "253"
45249       ],
45250       [
45251         "Dominica",
45252         "dm",
45253         "1767"
45254       ],
45255       [
45256         "Dominican Republic (República Dominicana)",
45257         "do",
45258         "1",
45259         2,
45260         ["809", "829", "849"]
45261       ],
45262       [
45263         "Ecuador",
45264         "ec",
45265         "593"
45266       ],
45267       [
45268         "Egypt (‫مصر‬‎)",
45269         "eg",
45270         "20"
45271       ],
45272       [
45273         "El Salvador",
45274         "sv",
45275         "503"
45276       ],
45277       [
45278         "Equatorial Guinea (Guinea Ecuatorial)",
45279         "gq",
45280         "240"
45281       ],
45282       [
45283         "Eritrea",
45284         "er",
45285         "291"
45286       ],
45287       [
45288         "Estonia (Eesti)",
45289         "ee",
45290         "372"
45291       ],
45292       [
45293         "Ethiopia",
45294         "et",
45295         "251"
45296       ],
45297       [
45298         "Falkland Islands (Islas Malvinas)",
45299         "fk",
45300         "500"
45301       ],
45302       [
45303         "Faroe Islands (Føroyar)",
45304         "fo",
45305         "298"
45306       ],
45307       [
45308         "Fiji",
45309         "fj",
45310         "679"
45311       ],
45312       [
45313         "Finland (Suomi)",
45314         "fi",
45315         "358",
45316         0
45317       ],
45318       [
45319         "France",
45320         "fr",
45321         "33"
45322       ],
45323       [
45324         "French Guiana (Guyane française)",
45325         "gf",
45326         "594"
45327       ],
45328       [
45329         "French Polynesia (Polynésie française)",
45330         "pf",
45331         "689"
45332       ],
45333       [
45334         "Gabon",
45335         "ga",
45336         "241"
45337       ],
45338       [
45339         "Gambia",
45340         "gm",
45341         "220"
45342       ],
45343       [
45344         "Georgia (საქართველო)",
45345         "ge",
45346         "995"
45347       ],
45348       [
45349         "Germany (Deutschland)",
45350         "de",
45351         "49"
45352       ],
45353       [
45354         "Ghana (Gaana)",
45355         "gh",
45356         "233"
45357       ],
45358       [
45359         "Gibraltar",
45360         "gi",
45361         "350"
45362       ],
45363       [
45364         "Greece (Ελλάδα)",
45365         "gr",
45366         "30"
45367       ],
45368       [
45369         "Greenland (Kalaallit Nunaat)",
45370         "gl",
45371         "299"
45372       ],
45373       [
45374         "Grenada",
45375         "gd",
45376         "1473"
45377       ],
45378       [
45379         "Guadeloupe",
45380         "gp",
45381         "590",
45382         0
45383       ],
45384       [
45385         "Guam",
45386         "gu",
45387         "1671"
45388       ],
45389       [
45390         "Guatemala",
45391         "gt",
45392         "502"
45393       ],
45394       [
45395         "Guernsey",
45396         "gg",
45397         "44",
45398         1
45399       ],
45400       [
45401         "Guinea (Guinée)",
45402         "gn",
45403         "224"
45404       ],
45405       [
45406         "Guinea-Bissau (Guiné Bissau)",
45407         "gw",
45408         "245"
45409       ],
45410       [
45411         "Guyana",
45412         "gy",
45413         "592"
45414       ],
45415       [
45416         "Haiti",
45417         "ht",
45418         "509"
45419       ],
45420       [
45421         "Honduras",
45422         "hn",
45423         "504"
45424       ],
45425       [
45426         "Hong Kong (香港)",
45427         "hk",
45428         "852"
45429       ],
45430       [
45431         "Hungary (Magyarország)",
45432         "hu",
45433         "36"
45434       ],
45435       [
45436         "Iceland (Ísland)",
45437         "is",
45438         "354"
45439       ],
45440       [
45441         "India (भारत)",
45442         "in",
45443         "91"
45444       ],
45445       [
45446         "Indonesia",
45447         "id",
45448         "62"
45449       ],
45450       [
45451         "Iran (‫ایران‬‎)",
45452         "ir",
45453         "98"
45454       ],
45455       [
45456         "Iraq (‫العراق‬‎)",
45457         "iq",
45458         "964"
45459       ],
45460       [
45461         "Ireland",
45462         "ie",
45463         "353"
45464       ],
45465       [
45466         "Isle of Man",
45467         "im",
45468         "44",
45469         2
45470       ],
45471       [
45472         "Israel (‫ישראל‬‎)",
45473         "il",
45474         "972"
45475       ],
45476       [
45477         "Italy (Italia)",
45478         "it",
45479         "39",
45480         0
45481       ],
45482       [
45483         "Jamaica",
45484         "jm",
45485         "1876"
45486       ],
45487       [
45488         "Japan (日本)",
45489         "jp",
45490         "81"
45491       ],
45492       [
45493         "Jersey",
45494         "je",
45495         "44",
45496         3
45497       ],
45498       [
45499         "Jordan (‫الأردن‬‎)",
45500         "jo",
45501         "962"
45502       ],
45503       [
45504         "Kazakhstan (Казахстан)",
45505         "kz",
45506         "7",
45507         1
45508       ],
45509       [
45510         "Kenya",
45511         "ke",
45512         "254"
45513       ],
45514       [
45515         "Kiribati",
45516         "ki",
45517         "686"
45518       ],
45519       [
45520         "Kosovo",
45521         "xk",
45522         "383"
45523       ],
45524       [
45525         "Kuwait (‫الكويت‬‎)",
45526         "kw",
45527         "965"
45528       ],
45529       [
45530         "Kyrgyzstan (Кыргызстан)",
45531         "kg",
45532         "996"
45533       ],
45534       [
45535         "Laos (ລາວ)",
45536         "la",
45537         "856"
45538       ],
45539       [
45540         "Latvia (Latvija)",
45541         "lv",
45542         "371"
45543       ],
45544       [
45545         "Lebanon (‫لبنان‬‎)",
45546         "lb",
45547         "961"
45548       ],
45549       [
45550         "Lesotho",
45551         "ls",
45552         "266"
45553       ],
45554       [
45555         "Liberia",
45556         "lr",
45557         "231"
45558       ],
45559       [
45560         "Libya (‫ليبيا‬‎)",
45561         "ly",
45562         "218"
45563       ],
45564       [
45565         "Liechtenstein",
45566         "li",
45567         "423"
45568       ],
45569       [
45570         "Lithuania (Lietuva)",
45571         "lt",
45572         "370"
45573       ],
45574       [
45575         "Luxembourg",
45576         "lu",
45577         "352"
45578       ],
45579       [
45580         "Macau (澳門)",
45581         "mo",
45582         "853"
45583       ],
45584       [
45585         "Macedonia (FYROM) (Македонија)",
45586         "mk",
45587         "389"
45588       ],
45589       [
45590         "Madagascar (Madagasikara)",
45591         "mg",
45592         "261"
45593       ],
45594       [
45595         "Malawi",
45596         "mw",
45597         "265"
45598       ],
45599       [
45600         "Malaysia",
45601         "my",
45602         "60"
45603       ],
45604       [
45605         "Maldives",
45606         "mv",
45607         "960"
45608       ],
45609       [
45610         "Mali",
45611         "ml",
45612         "223"
45613       ],
45614       [
45615         "Malta",
45616         "mt",
45617         "356"
45618       ],
45619       [
45620         "Marshall Islands",
45621         "mh",
45622         "692"
45623       ],
45624       [
45625         "Martinique",
45626         "mq",
45627         "596"
45628       ],
45629       [
45630         "Mauritania (‫موريتانيا‬‎)",
45631         "mr",
45632         "222"
45633       ],
45634       [
45635         "Mauritius (Moris)",
45636         "mu",
45637         "230"
45638       ],
45639       [
45640         "Mayotte",
45641         "yt",
45642         "262",
45643         1
45644       ],
45645       [
45646         "Mexico (México)",
45647         "mx",
45648         "52"
45649       ],
45650       [
45651         "Micronesia",
45652         "fm",
45653         "691"
45654       ],
45655       [
45656         "Moldova (Republica Moldova)",
45657         "md",
45658         "373"
45659       ],
45660       [
45661         "Monaco",
45662         "mc",
45663         "377"
45664       ],
45665       [
45666         "Mongolia (Монгол)",
45667         "mn",
45668         "976"
45669       ],
45670       [
45671         "Montenegro (Crna Gora)",
45672         "me",
45673         "382"
45674       ],
45675       [
45676         "Montserrat",
45677         "ms",
45678         "1664"
45679       ],
45680       [
45681         "Morocco (‫المغرب‬‎)",
45682         "ma",
45683         "212",
45684         0
45685       ],
45686       [
45687         "Mozambique (Moçambique)",
45688         "mz",
45689         "258"
45690       ],
45691       [
45692         "Myanmar (Burma) (မြန်မာ)",
45693         "mm",
45694         "95"
45695       ],
45696       [
45697         "Namibia (Namibië)",
45698         "na",
45699         "264"
45700       ],
45701       [
45702         "Nauru",
45703         "nr",
45704         "674"
45705       ],
45706       [
45707         "Nepal (नेपाल)",
45708         "np",
45709         "977"
45710       ],
45711       [
45712         "Netherlands (Nederland)",
45713         "nl",
45714         "31"
45715       ],
45716       [
45717         "New Caledonia (Nouvelle-Calédonie)",
45718         "nc",
45719         "687"
45720       ],
45721       [
45722         "New Zealand",
45723         "nz",
45724         "64"
45725       ],
45726       [
45727         "Nicaragua",
45728         "ni",
45729         "505"
45730       ],
45731       [
45732         "Niger (Nijar)",
45733         "ne",
45734         "227"
45735       ],
45736       [
45737         "Nigeria",
45738         "ng",
45739         "234"
45740       ],
45741       [
45742         "Niue",
45743         "nu",
45744         "683"
45745       ],
45746       [
45747         "Norfolk Island",
45748         "nf",
45749         "672"
45750       ],
45751       [
45752         "North Korea (조선 민주주의 인민 공화국)",
45753         "kp",
45754         "850"
45755       ],
45756       [
45757         "Northern Mariana Islands",
45758         "mp",
45759         "1670"
45760       ],
45761       [
45762         "Norway (Norge)",
45763         "no",
45764         "47",
45765         0
45766       ],
45767       [
45768         "Oman (‫عُمان‬‎)",
45769         "om",
45770         "968"
45771       ],
45772       [
45773         "Pakistan (‫پاکستان‬‎)",
45774         "pk",
45775         "92"
45776       ],
45777       [
45778         "Palau",
45779         "pw",
45780         "680"
45781       ],
45782       [
45783         "Palestine (‫فلسطين‬‎)",
45784         "ps",
45785         "970"
45786       ],
45787       [
45788         "Panama (Panamá)",
45789         "pa",
45790         "507"
45791       ],
45792       [
45793         "Papua New Guinea",
45794         "pg",
45795         "675"
45796       ],
45797       [
45798         "Paraguay",
45799         "py",
45800         "595"
45801       ],
45802       [
45803         "Peru (Perú)",
45804         "pe",
45805         "51"
45806       ],
45807       [
45808         "Philippines",
45809         "ph",
45810         "63"
45811       ],
45812       [
45813         "Poland (Polska)",
45814         "pl",
45815         "48"
45816       ],
45817       [
45818         "Portugal",
45819         "pt",
45820         "351"
45821       ],
45822       [
45823         "Puerto Rico",
45824         "pr",
45825         "1",
45826         3,
45827         ["787", "939"]
45828       ],
45829       [
45830         "Qatar (‫قطر‬‎)",
45831         "qa",
45832         "974"
45833       ],
45834       [
45835         "Réunion (La Réunion)",
45836         "re",
45837         "262",
45838         0
45839       ],
45840       [
45841         "Romania (România)",
45842         "ro",
45843         "40"
45844       ],
45845       [
45846         "Russia (Россия)",
45847         "ru",
45848         "7",
45849         0
45850       ],
45851       [
45852         "Rwanda",
45853         "rw",
45854         "250"
45855       ],
45856       [
45857         "Saint Barthélemy",
45858         "bl",
45859         "590",
45860         1
45861       ],
45862       [
45863         "Saint Helena",
45864         "sh",
45865         "290"
45866       ],
45867       [
45868         "Saint Kitts and Nevis",
45869         "kn",
45870         "1869"
45871       ],
45872       [
45873         "Saint Lucia",
45874         "lc",
45875         "1758"
45876       ],
45877       [
45878         "Saint Martin (Saint-Martin (partie française))",
45879         "mf",
45880         "590",
45881         2
45882       ],
45883       [
45884         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45885         "pm",
45886         "508"
45887       ],
45888       [
45889         "Saint Vincent and the Grenadines",
45890         "vc",
45891         "1784"
45892       ],
45893       [
45894         "Samoa",
45895         "ws",
45896         "685"
45897       ],
45898       [
45899         "San Marino",
45900         "sm",
45901         "378"
45902       ],
45903       [
45904         "São Tomé and Príncipe (São Tomé e Príncipe)",
45905         "st",
45906         "239"
45907       ],
45908       [
45909         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45910         "sa",
45911         "966"
45912       ],
45913       [
45914         "Senegal (Sénégal)",
45915         "sn",
45916         "221"
45917       ],
45918       [
45919         "Serbia (Србија)",
45920         "rs",
45921         "381"
45922       ],
45923       [
45924         "Seychelles",
45925         "sc",
45926         "248"
45927       ],
45928       [
45929         "Sierra Leone",
45930         "sl",
45931         "232"
45932       ],
45933       [
45934         "Singapore",
45935         "sg",
45936         "65"
45937       ],
45938       [
45939         "Sint Maarten",
45940         "sx",
45941         "1721"
45942       ],
45943       [
45944         "Slovakia (Slovensko)",
45945         "sk",
45946         "421"
45947       ],
45948       [
45949         "Slovenia (Slovenija)",
45950         "si",
45951         "386"
45952       ],
45953       [
45954         "Solomon Islands",
45955         "sb",
45956         "677"
45957       ],
45958       [
45959         "Somalia (Soomaaliya)",
45960         "so",
45961         "252"
45962       ],
45963       [
45964         "South Africa",
45965         "za",
45966         "27"
45967       ],
45968       [
45969         "South Korea (대한민국)",
45970         "kr",
45971         "82"
45972       ],
45973       [
45974         "South Sudan (‫جنوب السودان‬‎)",
45975         "ss",
45976         "211"
45977       ],
45978       [
45979         "Spain (España)",
45980         "es",
45981         "34"
45982       ],
45983       [
45984         "Sri Lanka (ශ්‍රී ලංකාව)",
45985         "lk",
45986         "94"
45987       ],
45988       [
45989         "Sudan (‫السودان‬‎)",
45990         "sd",
45991         "249"
45992       ],
45993       [
45994         "Suriname",
45995         "sr",
45996         "597"
45997       ],
45998       [
45999         "Svalbard and Jan Mayen",
46000         "sj",
46001         "47",
46002         1
46003       ],
46004       [
46005         "Swaziland",
46006         "sz",
46007         "268"
46008       ],
46009       [
46010         "Sweden (Sverige)",
46011         "se",
46012         "46"
46013       ],
46014       [
46015         "Switzerland (Schweiz)",
46016         "ch",
46017         "41"
46018       ],
46019       [
46020         "Syria (‫سوريا‬‎)",
46021         "sy",
46022         "963"
46023       ],
46024       [
46025         "Taiwan (台灣)",
46026         "tw",
46027         "886"
46028       ],
46029       [
46030         "Tajikistan",
46031         "tj",
46032         "992"
46033       ],
46034       [
46035         "Tanzania",
46036         "tz",
46037         "255"
46038       ],
46039       [
46040         "Thailand (ไทย)",
46041         "th",
46042         "66"
46043       ],
46044       [
46045         "Timor-Leste",
46046         "tl",
46047         "670"
46048       ],
46049       [
46050         "Togo",
46051         "tg",
46052         "228"
46053       ],
46054       [
46055         "Tokelau",
46056         "tk",
46057         "690"
46058       ],
46059       [
46060         "Tonga",
46061         "to",
46062         "676"
46063       ],
46064       [
46065         "Trinidad and Tobago",
46066         "tt",
46067         "1868"
46068       ],
46069       [
46070         "Tunisia (‫تونس‬‎)",
46071         "tn",
46072         "216"
46073       ],
46074       [
46075         "Turkey (Türkiye)",
46076         "tr",
46077         "90"
46078       ],
46079       [
46080         "Turkmenistan",
46081         "tm",
46082         "993"
46083       ],
46084       [
46085         "Turks and Caicos Islands",
46086         "tc",
46087         "1649"
46088       ],
46089       [
46090         "Tuvalu",
46091         "tv",
46092         "688"
46093       ],
46094       [
46095         "U.S. Virgin Islands",
46096         "vi",
46097         "1340"
46098       ],
46099       [
46100         "Uganda",
46101         "ug",
46102         "256"
46103       ],
46104       [
46105         "Ukraine (Україна)",
46106         "ua",
46107         "380"
46108       ],
46109       [
46110         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46111         "ae",
46112         "971"
46113       ],
46114       [
46115         "United Kingdom",
46116         "gb",
46117         "44",
46118         0
46119       ],
46120       [
46121         "United States",
46122         "us",
46123         "1",
46124         0
46125       ],
46126       [
46127         "Uruguay",
46128         "uy",
46129         "598"
46130       ],
46131       [
46132         "Uzbekistan (Oʻzbekiston)",
46133         "uz",
46134         "998"
46135       ],
46136       [
46137         "Vanuatu",
46138         "vu",
46139         "678"
46140       ],
46141       [
46142         "Vatican City (Città del Vaticano)",
46143         "va",
46144         "39",
46145         1
46146       ],
46147       [
46148         "Venezuela",
46149         "ve",
46150         "58"
46151       ],
46152       [
46153         "Vietnam (Việt Nam)",
46154         "vn",
46155         "84"
46156       ],
46157       [
46158         "Wallis and Futuna (Wallis-et-Futuna)",
46159         "wf",
46160         "681"
46161       ],
46162       [
46163         "Western Sahara (‫الصحراء الغربية‬‎)",
46164         "eh",
46165         "212",
46166         1
46167       ],
46168       [
46169         "Yemen (‫اليمن‬‎)",
46170         "ye",
46171         "967"
46172       ],
46173       [
46174         "Zambia",
46175         "zm",
46176         "260"
46177       ],
46178       [
46179         "Zimbabwe",
46180         "zw",
46181         "263"
46182       ],
46183       [
46184         "Åland Islands",
46185         "ax",
46186         "358",
46187         1
46188       ]
46189   ];
46190   
46191   return d;
46192 }/**
46193 *    This script refer to:
46194 *    Title: International Telephone Input
46195 *    Author: Jack O'Connor
46196 *    Code version:  v12.1.12
46197 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46198 **/
46199
46200 /**
46201  * @class Roo.bootstrap.form.PhoneInput
46202  * @extends Roo.bootstrap.form.TriggerField
46203  * An input with International dial-code selection
46204  
46205  * @cfg {String} defaultDialCode default '+852'
46206  * @cfg {Array} preferedCountries default []
46207   
46208  * @constructor
46209  * Create a new PhoneInput.
46210  * @param {Object} config Configuration options
46211  */
46212
46213 Roo.bootstrap.form.PhoneInput = function(config) {
46214     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46215 };
46216
46217 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46218         /**
46219         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46220         */
46221         listWidth: undefined,
46222         
46223         selectedClass: 'active',
46224         
46225         invalidClass : "has-warning",
46226         
46227         validClass: 'has-success',
46228         
46229         allowed: '0123456789',
46230         
46231         max_length: 15,
46232         
46233         /**
46234          * @cfg {String} defaultDialCode The default dial code when initializing the input
46235          */
46236         defaultDialCode: '+852',
46237         
46238         /**
46239          * @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
46240          */
46241         preferedCountries: false,
46242         
46243         getAutoCreate : function()
46244         {
46245             var data = Roo.bootstrap.form.PhoneInputData();
46246             var align = this.labelAlign || this.parentLabelAlign();
46247             var id = Roo.id();
46248             
46249             this.allCountries = [];
46250             this.dialCodeMapping = [];
46251             
46252             for (var i = 0; i < data.length; i++) {
46253               var c = data[i];
46254               this.allCountries[i] = {
46255                 name: c[0],
46256                 iso2: c[1],
46257                 dialCode: c[2],
46258                 priority: c[3] || 0,
46259                 areaCodes: c[4] || null
46260               };
46261               this.dialCodeMapping[c[2]] = {
46262                   name: c[0],
46263                   iso2: c[1],
46264                   priority: c[3] || 0,
46265                   areaCodes: c[4] || null
46266               };
46267             }
46268             
46269             var cfg = {
46270                 cls: 'form-group',
46271                 cn: []
46272             };
46273             
46274             var input =  {
46275                 tag: 'input',
46276                 id : id,
46277                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46278                 maxlength: this.max_length,
46279                 cls : 'form-control tel-input',
46280                 autocomplete: 'new-password'
46281             };
46282             
46283             var hiddenInput = {
46284                 tag: 'input',
46285                 type: 'hidden',
46286                 cls: 'hidden-tel-input'
46287             };
46288             
46289             if (this.name) {
46290                 hiddenInput.name = this.name;
46291             }
46292             
46293             if (this.disabled) {
46294                 input.disabled = true;
46295             }
46296             
46297             var flag_container = {
46298                 tag: 'div',
46299                 cls: 'flag-box',
46300                 cn: [
46301                     {
46302                         tag: 'div',
46303                         cls: 'flag'
46304                     },
46305                     {
46306                         tag: 'div',
46307                         cls: 'caret'
46308                     }
46309                 ]
46310             };
46311             
46312             var box = {
46313                 tag: 'div',
46314                 cls: this.hasFeedback ? 'has-feedback' : '',
46315                 cn: [
46316                     hiddenInput,
46317                     input,
46318                     {
46319                         tag: 'input',
46320                         cls: 'dial-code-holder',
46321                         disabled: true
46322                     }
46323                 ]
46324             };
46325             
46326             var container = {
46327                 cls: 'roo-select2-container input-group',
46328                 cn: [
46329                     flag_container,
46330                     box
46331                 ]
46332             };
46333             
46334             if (this.fieldLabel.length) {
46335                 var indicator = {
46336                     tag: 'i',
46337                     tooltip: 'This field is required'
46338                 };
46339                 
46340                 var label = {
46341                     tag: 'label',
46342                     'for':  id,
46343                     cls: 'control-label',
46344                     cn: []
46345                 };
46346                 
46347                 var label_text = {
46348                     tag: 'span',
46349                     html: this.fieldLabel
46350                 };
46351                 
46352                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46353                 label.cn = [
46354                     indicator,
46355                     label_text
46356                 ];
46357                 
46358                 if(this.indicatorpos == 'right') {
46359                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46360                     label.cn = [
46361                         label_text,
46362                         indicator
46363                     ];
46364                 }
46365                 
46366                 if(align == 'left') {
46367                     container = {
46368                         tag: 'div',
46369                         cn: [
46370                             container
46371                         ]
46372                     };
46373                     
46374                     if(this.labelWidth > 12){
46375                         label.style = "width: " + this.labelWidth + 'px';
46376                     }
46377                     if(this.labelWidth < 13 && this.labelmd == 0){
46378                         this.labelmd = this.labelWidth;
46379                     }
46380                     if(this.labellg > 0){
46381                         label.cls += ' col-lg-' + this.labellg;
46382                         input.cls += ' col-lg-' + (12 - this.labellg);
46383                     }
46384                     if(this.labelmd > 0){
46385                         label.cls += ' col-md-' + this.labelmd;
46386                         container.cls += ' col-md-' + (12 - this.labelmd);
46387                     }
46388                     if(this.labelsm > 0){
46389                         label.cls += ' col-sm-' + this.labelsm;
46390                         container.cls += ' col-sm-' + (12 - this.labelsm);
46391                     }
46392                     if(this.labelxs > 0){
46393                         label.cls += ' col-xs-' + this.labelxs;
46394                         container.cls += ' col-xs-' + (12 - this.labelxs);
46395                     }
46396                 }
46397             }
46398             
46399             cfg.cn = [
46400                 label,
46401                 container
46402             ];
46403             
46404             var settings = this;
46405             
46406             ['xs','sm','md','lg'].map(function(size){
46407                 if (settings[size]) {
46408                     cfg.cls += ' col-' + size + '-' + settings[size];
46409                 }
46410             });
46411             
46412             this.store = new Roo.data.Store({
46413                 proxy : new Roo.data.MemoryProxy({}),
46414                 reader : new Roo.data.JsonReader({
46415                     fields : [
46416                         {
46417                             'name' : 'name',
46418                             'type' : 'string'
46419                         },
46420                         {
46421                             'name' : 'iso2',
46422                             'type' : 'string'
46423                         },
46424                         {
46425                             'name' : 'dialCode',
46426                             'type' : 'string'
46427                         },
46428                         {
46429                             'name' : 'priority',
46430                             'type' : 'string'
46431                         },
46432                         {
46433                             'name' : 'areaCodes',
46434                             'type' : 'string'
46435                         }
46436                     ]
46437                 })
46438             });
46439             
46440             if(!this.preferedCountries) {
46441                 this.preferedCountries = [
46442                     'hk',
46443                     'gb',
46444                     'us'
46445                 ];
46446             }
46447             
46448             var p = this.preferedCountries.reverse();
46449             
46450             if(p) {
46451                 for (var i = 0; i < p.length; i++) {
46452                     for (var j = 0; j < this.allCountries.length; j++) {
46453                         if(this.allCountries[j].iso2 == p[i]) {
46454                             var t = this.allCountries[j];
46455                             this.allCountries.splice(j,1);
46456                             this.allCountries.unshift(t);
46457                         }
46458                     } 
46459                 }
46460             }
46461             
46462             this.store.proxy.data = {
46463                 success: true,
46464                 data: this.allCountries
46465             };
46466             
46467             return cfg;
46468         },
46469         
46470         initEvents : function()
46471         {
46472             this.createList();
46473             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46474             
46475             this.indicator = this.indicatorEl();
46476             this.flag = this.flagEl();
46477             this.dialCodeHolder = this.dialCodeHolderEl();
46478             
46479             this.trigger = this.el.select('div.flag-box',true).first();
46480             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46481             
46482             var _this = this;
46483             
46484             (function(){
46485                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46486                 _this.list.setWidth(lw);
46487             }).defer(100);
46488             
46489             this.list.on('mouseover', this.onViewOver, this);
46490             this.list.on('mousemove', this.onViewMove, this);
46491             this.inputEl().on("keyup", this.onKeyUp, this);
46492             this.inputEl().on("keypress", this.onKeyPress, this);
46493             
46494             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46495
46496             this.view = new Roo.View(this.list, this.tpl, {
46497                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46498             });
46499             
46500             this.view.on('click', this.onViewClick, this);
46501             this.setValue(this.defaultDialCode);
46502         },
46503         
46504         onTriggerClick : function(e)
46505         {
46506             Roo.log('trigger click');
46507             if(this.disabled){
46508                 return;
46509             }
46510             
46511             if(this.isExpanded()){
46512                 this.collapse();
46513                 this.hasFocus = false;
46514             }else {
46515                 this.store.load({});
46516                 this.hasFocus = true;
46517                 this.expand();
46518             }
46519         },
46520         
46521         isExpanded : function()
46522         {
46523             return this.list.isVisible();
46524         },
46525         
46526         collapse : function()
46527         {
46528             if(!this.isExpanded()){
46529                 return;
46530             }
46531             this.list.hide();
46532             Roo.get(document).un('mousedown', this.collapseIf, this);
46533             Roo.get(document).un('mousewheel', this.collapseIf, this);
46534             this.fireEvent('collapse', this);
46535             this.validate();
46536         },
46537         
46538         expand : function()
46539         {
46540             Roo.log('expand');
46541
46542             if(this.isExpanded() || !this.hasFocus){
46543                 return;
46544             }
46545             
46546             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46547             this.list.setWidth(lw);
46548             
46549             this.list.show();
46550             this.restrictHeight();
46551             
46552             Roo.get(document).on('mousedown', this.collapseIf, this);
46553             Roo.get(document).on('mousewheel', this.collapseIf, this);
46554             
46555             this.fireEvent('expand', this);
46556         },
46557         
46558         restrictHeight : function()
46559         {
46560             this.list.alignTo(this.inputEl(), this.listAlign);
46561             this.list.alignTo(this.inputEl(), this.listAlign);
46562         },
46563         
46564         onViewOver : function(e, t)
46565         {
46566             if(this.inKeyMode){
46567                 return;
46568             }
46569             var item = this.view.findItemFromChild(t);
46570             
46571             if(item){
46572                 var index = this.view.indexOf(item);
46573                 this.select(index, false);
46574             }
46575         },
46576
46577         // private
46578         onViewClick : function(view, doFocus, el, e)
46579         {
46580             var index = this.view.getSelectedIndexes()[0];
46581             
46582             var r = this.store.getAt(index);
46583             
46584             if(r){
46585                 this.onSelect(r, index);
46586             }
46587             if(doFocus !== false && !this.blockFocus){
46588                 this.inputEl().focus();
46589             }
46590         },
46591         
46592         onViewMove : function(e, t)
46593         {
46594             this.inKeyMode = false;
46595         },
46596         
46597         select : function(index, scrollIntoView)
46598         {
46599             this.selectedIndex = index;
46600             this.view.select(index);
46601             if(scrollIntoView !== false){
46602                 var el = this.view.getNode(index);
46603                 if(el){
46604                     this.list.scrollChildIntoView(el, false);
46605                 }
46606             }
46607         },
46608         
46609         createList : function()
46610         {
46611             this.list = Roo.get(document.body).createChild({
46612                 tag: 'ul',
46613                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46614                 style: 'display:none'
46615             });
46616             
46617             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46618         },
46619         
46620         collapseIf : function(e)
46621         {
46622             var in_combo  = e.within(this.el);
46623             var in_list =  e.within(this.list);
46624             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46625             
46626             if (in_combo || in_list || is_list) {
46627                 return;
46628             }
46629             this.collapse();
46630         },
46631         
46632         onSelect : function(record, index)
46633         {
46634             if(this.fireEvent('beforeselect', this, record, index) !== false){
46635                 
46636                 this.setFlagClass(record.data.iso2);
46637                 this.setDialCode(record.data.dialCode);
46638                 this.hasFocus = false;
46639                 this.collapse();
46640                 this.fireEvent('select', this, record, index);
46641             }
46642         },
46643         
46644         flagEl : function()
46645         {
46646             var flag = this.el.select('div.flag',true).first();
46647             if(!flag){
46648                 return false;
46649             }
46650             return flag;
46651         },
46652         
46653         dialCodeHolderEl : function()
46654         {
46655             var d = this.el.select('input.dial-code-holder',true).first();
46656             if(!d){
46657                 return false;
46658             }
46659             return d;
46660         },
46661         
46662         setDialCode : function(v)
46663         {
46664             this.dialCodeHolder.dom.value = '+'+v;
46665         },
46666         
46667         setFlagClass : function(n)
46668         {
46669             this.flag.dom.className = 'flag '+n;
46670         },
46671         
46672         getValue : function()
46673         {
46674             var v = this.inputEl().getValue();
46675             if(this.dialCodeHolder) {
46676                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46677             }
46678             return v;
46679         },
46680         
46681         setValue : function(v)
46682         {
46683             var d = this.getDialCode(v);
46684             
46685             //invalid dial code
46686             if(v.length == 0 || !d || d.length == 0) {
46687                 if(this.rendered){
46688                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46689                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46690                 }
46691                 return;
46692             }
46693             
46694             //valid dial code
46695             this.setFlagClass(this.dialCodeMapping[d].iso2);
46696             this.setDialCode(d);
46697             this.inputEl().dom.value = v.replace('+'+d,'');
46698             this.hiddenEl().dom.value = this.getValue();
46699             
46700             this.validate();
46701         },
46702         
46703         getDialCode : function(v)
46704         {
46705             v = v ||  '';
46706             
46707             if (v.length == 0) {
46708                 return this.dialCodeHolder.dom.value;
46709             }
46710             
46711             var dialCode = "";
46712             if (v.charAt(0) != "+") {
46713                 return false;
46714             }
46715             var numericChars = "";
46716             for (var i = 1; i < v.length; i++) {
46717               var c = v.charAt(i);
46718               if (!isNaN(c)) {
46719                 numericChars += c;
46720                 if (this.dialCodeMapping[numericChars]) {
46721                   dialCode = v.substr(1, i);
46722                 }
46723                 if (numericChars.length == 4) {
46724                   break;
46725                 }
46726               }
46727             }
46728             return dialCode;
46729         },
46730         
46731         reset : function()
46732         {
46733             this.setValue(this.defaultDialCode);
46734             this.validate();
46735         },
46736         
46737         hiddenEl : function()
46738         {
46739             return this.el.select('input.hidden-tel-input',true).first();
46740         },
46741         
46742         // after setting val
46743         onKeyUp : function(e){
46744             this.setValue(this.getValue());
46745         },
46746         
46747         onKeyPress : function(e){
46748             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46749                 e.stopEvent();
46750             }
46751         }
46752         
46753 });
46754 /**
46755  * @class Roo.bootstrap.form.MoneyField
46756  * @extends Roo.bootstrap.form.ComboBox
46757  * Bootstrap MoneyField class
46758  * 
46759  * @constructor
46760  * Create a new MoneyField.
46761  * @param {Object} config Configuration options
46762  */
46763
46764 Roo.bootstrap.form.MoneyField = function(config) {
46765     
46766     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46767     
46768 };
46769
46770 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46771     
46772     /**
46773      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46774      */
46775     allowDecimals : true,
46776     /**
46777      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46778      */
46779     decimalSeparator : ".",
46780     /**
46781      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46782      */
46783     decimalPrecision : 0,
46784     /**
46785      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46786      */
46787     allowNegative : true,
46788     /**
46789      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46790      */
46791     allowZero: true,
46792     /**
46793      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46794      */
46795     minValue : Number.NEGATIVE_INFINITY,
46796     /**
46797      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46798      */
46799     maxValue : Number.MAX_VALUE,
46800     /**
46801      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46802      */
46803     minText : "The minimum value for this field is {0}",
46804     /**
46805      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46806      */
46807     maxText : "The maximum value for this field is {0}",
46808     /**
46809      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46810      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46811      */
46812     nanText : "{0} is not a valid number",
46813     /**
46814      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46815      */
46816     castInt : true,
46817     /**
46818      * @cfg {String} defaults currency of the MoneyField
46819      * value should be in lkey
46820      */
46821     defaultCurrency : false,
46822     /**
46823      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46824      */
46825     thousandsDelimiter : false,
46826     /**
46827      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46828      */
46829     max_length: false,
46830     
46831     inputlg : 9,
46832     inputmd : 9,
46833     inputsm : 9,
46834     inputxs : 6,
46835      /**
46836      * @cfg {Roo.data.Store} store  Store to lookup currency??
46837      */
46838     store : false,
46839     
46840     getAutoCreate : function()
46841     {
46842         var align = this.labelAlign || this.parentLabelAlign();
46843         
46844         var id = Roo.id();
46845
46846         var cfg = {
46847             cls: 'form-group',
46848             cn: []
46849         };
46850
46851         var input =  {
46852             tag: 'input',
46853             id : id,
46854             cls : 'form-control roo-money-amount-input',
46855             autocomplete: 'new-password'
46856         };
46857         
46858         var hiddenInput = {
46859             tag: 'input',
46860             type: 'hidden',
46861             id: Roo.id(),
46862             cls: 'hidden-number-input'
46863         };
46864         
46865         if(this.max_length) {
46866             input.maxlength = this.max_length; 
46867         }
46868         
46869         if (this.name) {
46870             hiddenInput.name = this.name;
46871         }
46872
46873         if (this.disabled) {
46874             input.disabled = true;
46875         }
46876
46877         var clg = 12 - this.inputlg;
46878         var cmd = 12 - this.inputmd;
46879         var csm = 12 - this.inputsm;
46880         var cxs = 12 - this.inputxs;
46881         
46882         var container = {
46883             tag : 'div',
46884             cls : 'row roo-money-field',
46885             cn : [
46886                 {
46887                     tag : 'div',
46888                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46889                     cn : [
46890                         {
46891                             tag : 'div',
46892                             cls: 'roo-select2-container input-group',
46893                             cn: [
46894                                 {
46895                                     tag : 'input',
46896                                     cls : 'form-control roo-money-currency-input',
46897                                     autocomplete: 'new-password',
46898                                     readOnly : 1,
46899                                     name : this.currencyName
46900                                 },
46901                                 {
46902                                     tag :'span',
46903                                     cls : 'input-group-addon',
46904                                     cn : [
46905                                         {
46906                                             tag: 'span',
46907                                             cls: 'caret'
46908                                         }
46909                                     ]
46910                                 }
46911                             ]
46912                         }
46913                     ]
46914                 },
46915                 {
46916                     tag : 'div',
46917                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46918                     cn : [
46919                         {
46920                             tag: 'div',
46921                             cls: this.hasFeedback ? 'has-feedback' : '',
46922                             cn: [
46923                                 input
46924                             ]
46925                         }
46926                     ]
46927                 }
46928             ]
46929             
46930         };
46931         
46932         if (this.fieldLabel.length) {
46933             var indicator = {
46934                 tag: 'i',
46935                 tooltip: 'This field is required'
46936             };
46937
46938             var label = {
46939                 tag: 'label',
46940                 'for':  id,
46941                 cls: 'control-label',
46942                 cn: []
46943             };
46944
46945             var label_text = {
46946                 tag: 'span',
46947                 html: this.fieldLabel
46948             };
46949
46950             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46951             label.cn = [
46952                 indicator,
46953                 label_text
46954             ];
46955
46956             if(this.indicatorpos == 'right') {
46957                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46958                 label.cn = [
46959                     label_text,
46960                     indicator
46961                 ];
46962             }
46963
46964             if(align == 'left') {
46965                 container = {
46966                     tag: 'div',
46967                     cn: [
46968                         container
46969                     ]
46970                 };
46971
46972                 if(this.labelWidth > 12){
46973                     label.style = "width: " + this.labelWidth + 'px';
46974                 }
46975                 if(this.labelWidth < 13 && this.labelmd == 0){
46976                     this.labelmd = this.labelWidth;
46977                 }
46978                 if(this.labellg > 0){
46979                     label.cls += ' col-lg-' + this.labellg;
46980                     input.cls += ' col-lg-' + (12 - this.labellg);
46981                 }
46982                 if(this.labelmd > 0){
46983                     label.cls += ' col-md-' + this.labelmd;
46984                     container.cls += ' col-md-' + (12 - this.labelmd);
46985                 }
46986                 if(this.labelsm > 0){
46987                     label.cls += ' col-sm-' + this.labelsm;
46988                     container.cls += ' col-sm-' + (12 - this.labelsm);
46989                 }
46990                 if(this.labelxs > 0){
46991                     label.cls += ' col-xs-' + this.labelxs;
46992                     container.cls += ' col-xs-' + (12 - this.labelxs);
46993                 }
46994             }
46995         }
46996
46997         cfg.cn = [
46998             label,
46999             container,
47000             hiddenInput
47001         ];
47002         
47003         var settings = this;
47004
47005         ['xs','sm','md','lg'].map(function(size){
47006             if (settings[size]) {
47007                 cfg.cls += ' col-' + size + '-' + settings[size];
47008             }
47009         });
47010         
47011         return cfg;
47012     },
47013     
47014     initEvents : function()
47015     {
47016         this.indicator = this.indicatorEl();
47017         
47018         this.initCurrencyEvent();
47019         
47020         this.initNumberEvent();
47021     },
47022     
47023     initCurrencyEvent : function()
47024     {
47025         if (!this.store) {
47026             throw "can not find store for combo";
47027         }
47028         
47029         this.store = Roo.factory(this.store, Roo.data);
47030         this.store.parent = this;
47031         
47032         this.createList();
47033         
47034         this.triggerEl = this.el.select('.input-group-addon', true).first();
47035         
47036         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47037         
47038         var _this = this;
47039         
47040         (function(){
47041             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47042             _this.list.setWidth(lw);
47043         }).defer(100);
47044         
47045         this.list.on('mouseover', this.onViewOver, this);
47046         this.list.on('mousemove', this.onViewMove, this);
47047         this.list.on('scroll', this.onViewScroll, this);
47048         
47049         if(!this.tpl){
47050             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47051         }
47052         
47053         this.view = new Roo.View(this.list, this.tpl, {
47054             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47055         });
47056         
47057         this.view.on('click', this.onViewClick, this);
47058         
47059         this.store.on('beforeload', this.onBeforeLoad, this);
47060         this.store.on('load', this.onLoad, this);
47061         this.store.on('loadexception', this.onLoadException, this);
47062         
47063         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47064             "up" : function(e){
47065                 this.inKeyMode = true;
47066                 this.selectPrev();
47067             },
47068
47069             "down" : function(e){
47070                 if(!this.isExpanded()){
47071                     this.onTriggerClick();
47072                 }else{
47073                     this.inKeyMode = true;
47074                     this.selectNext();
47075                 }
47076             },
47077
47078             "enter" : function(e){
47079                 this.collapse();
47080                 
47081                 if(this.fireEvent("specialkey", this, e)){
47082                     this.onViewClick(false);
47083                 }
47084                 
47085                 return true;
47086             },
47087
47088             "esc" : function(e){
47089                 this.collapse();
47090             },
47091
47092             "tab" : function(e){
47093                 this.collapse();
47094                 
47095                 if(this.fireEvent("specialkey", this, e)){
47096                     this.onViewClick(false);
47097                 }
47098                 
47099                 return true;
47100             },
47101
47102             scope : this,
47103
47104             doRelay : function(foo, bar, hname){
47105                 if(hname == 'down' || this.scope.isExpanded()){
47106                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47107                 }
47108                 return true;
47109             },
47110
47111             forceKeyDown: true
47112         });
47113         
47114         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47115         
47116     },
47117     
47118     initNumberEvent : function(e)
47119     {
47120         this.inputEl().on("keydown" , this.fireKey,  this);
47121         this.inputEl().on("focus", this.onFocus,  this);
47122         this.inputEl().on("blur", this.onBlur,  this);
47123         
47124         this.inputEl().relayEvent('keyup', this);
47125         
47126         if(this.indicator){
47127             this.indicator.addClass('invisible');
47128         }
47129  
47130         this.originalValue = this.getValue();
47131         
47132         if(this.validationEvent == 'keyup'){
47133             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47134             this.inputEl().on('keyup', this.filterValidation, this);
47135         }
47136         else if(this.validationEvent !== false){
47137             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47138         }
47139         
47140         if(this.selectOnFocus){
47141             this.on("focus", this.preFocus, this);
47142             
47143         }
47144         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47145             this.inputEl().on("keypress", this.filterKeys, this);
47146         } else {
47147             this.inputEl().relayEvent('keypress', this);
47148         }
47149         
47150         var allowed = "0123456789";
47151         
47152         if(this.allowDecimals){
47153             allowed += this.decimalSeparator;
47154         }
47155         
47156         if(this.allowNegative){
47157             allowed += "-";
47158         }
47159         
47160         if(this.thousandsDelimiter) {
47161             allowed += ",";
47162         }
47163         
47164         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47165         
47166         var keyPress = function(e){
47167             
47168             var k = e.getKey();
47169             
47170             var c = e.getCharCode();
47171             
47172             if(
47173                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47174                     allowed.indexOf(String.fromCharCode(c)) === -1
47175             ){
47176                 e.stopEvent();
47177                 return;
47178             }
47179             
47180             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47181                 return;
47182             }
47183             
47184             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47185                 e.stopEvent();
47186             }
47187         };
47188         
47189         this.inputEl().on("keypress", keyPress, this);
47190         
47191     },
47192     
47193     onTriggerClick : function(e)
47194     {   
47195         if(this.disabled){
47196             return;
47197         }
47198         
47199         this.page = 0;
47200         this.loadNext = false;
47201         
47202         if(this.isExpanded()){
47203             this.collapse();
47204             return;
47205         }
47206         
47207         this.hasFocus = true;
47208         
47209         if(this.triggerAction == 'all') {
47210             this.doQuery(this.allQuery, true);
47211             return;
47212         }
47213         
47214         this.doQuery(this.getRawValue());
47215     },
47216     
47217     getCurrency : function()
47218     {   
47219         var v = this.currencyEl().getValue();
47220         
47221         return v;
47222     },
47223     
47224     restrictHeight : function()
47225     {
47226         this.list.alignTo(this.currencyEl(), this.listAlign);
47227         this.list.alignTo(this.currencyEl(), this.listAlign);
47228     },
47229     
47230     onViewClick : function(view, doFocus, el, e)
47231     {
47232         var index = this.view.getSelectedIndexes()[0];
47233         
47234         var r = this.store.getAt(index);
47235         
47236         if(r){
47237             this.onSelect(r, index);
47238         }
47239     },
47240     
47241     onSelect : function(record, index){
47242         
47243         if(this.fireEvent('beforeselect', this, record, index) !== false){
47244         
47245             this.setFromCurrencyData(index > -1 ? record.data : false);
47246             
47247             this.collapse();
47248             
47249             this.fireEvent('select', this, record, index);
47250         }
47251     },
47252     
47253     setFromCurrencyData : function(o)
47254     {
47255         var currency = '';
47256         
47257         this.lastCurrency = o;
47258         
47259         if (this.currencyField) {
47260             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47261         } else {
47262             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47263         }
47264         
47265         this.lastSelectionText = currency;
47266         
47267         //setting default currency
47268         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47269             this.setCurrency(this.defaultCurrency);
47270             return;
47271         }
47272         
47273         this.setCurrency(currency);
47274     },
47275     
47276     setFromData : function(o)
47277     {
47278         var c = {};
47279         
47280         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47281         
47282         this.setFromCurrencyData(c);
47283         
47284         var value = '';
47285         
47286         if (this.name) {
47287             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47288         } else {
47289             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47290         }
47291         
47292         this.setValue(value);
47293         
47294     },
47295     
47296     setCurrency : function(v)
47297     {   
47298         this.currencyValue = v;
47299         
47300         if(this.rendered){
47301             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47302             this.validate();
47303         }
47304     },
47305     
47306     setValue : function(v)
47307     {
47308         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47309         
47310         this.value = v;
47311         
47312         if(this.rendered){
47313             
47314             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47315             
47316             this.inputEl().dom.value = (v == '') ? '' :
47317                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47318             
47319             if(!this.allowZero && v === '0') {
47320                 this.hiddenEl().dom.value = '';
47321                 this.inputEl().dom.value = '';
47322             }
47323             
47324             this.validate();
47325         }
47326     },
47327     
47328     getRawValue : function()
47329     {
47330         var v = this.inputEl().getValue();
47331         
47332         return v;
47333     },
47334     
47335     getValue : function()
47336     {
47337         return this.fixPrecision(this.parseValue(this.getRawValue()));
47338     },
47339     
47340     parseValue : function(value)
47341     {
47342         if(this.thousandsDelimiter) {
47343             value += "";
47344             r = new RegExp(",", "g");
47345             value = value.replace(r, "");
47346         }
47347         
47348         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47349         return isNaN(value) ? '' : value;
47350         
47351     },
47352     
47353     fixPrecision : function(value)
47354     {
47355         if(this.thousandsDelimiter) {
47356             value += "";
47357             r = new RegExp(",", "g");
47358             value = value.replace(r, "");
47359         }
47360         
47361         var nan = isNaN(value);
47362         
47363         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47364             return nan ? '' : value;
47365         }
47366         return parseFloat(value).toFixed(this.decimalPrecision);
47367     },
47368     
47369     decimalPrecisionFcn : function(v)
47370     {
47371         return Math.floor(v);
47372     },
47373     
47374     validateValue : function(value)
47375     {
47376         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47377             return false;
47378         }
47379         
47380         var num = this.parseValue(value);
47381         
47382         if(isNaN(num)){
47383             this.markInvalid(String.format(this.nanText, value));
47384             return false;
47385         }
47386         
47387         if(num < this.minValue){
47388             this.markInvalid(String.format(this.minText, this.minValue));
47389             return false;
47390         }
47391         
47392         if(num > this.maxValue){
47393             this.markInvalid(String.format(this.maxText, this.maxValue));
47394             return false;
47395         }
47396         
47397         return true;
47398     },
47399     
47400     validate : function()
47401     {
47402         if(this.disabled || this.allowBlank){
47403             this.markValid();
47404             return true;
47405         }
47406         
47407         var currency = this.getCurrency();
47408         
47409         if(this.validateValue(this.getRawValue()) && currency.length){
47410             this.markValid();
47411             return true;
47412         }
47413         
47414         this.markInvalid();
47415         return false;
47416     },
47417     
47418     getName: function()
47419     {
47420         return this.name;
47421     },
47422     
47423     beforeBlur : function()
47424     {
47425         if(!this.castInt){
47426             return;
47427         }
47428         
47429         var v = this.parseValue(this.getRawValue());
47430         
47431         if(v || v == 0){
47432             this.setValue(v);
47433         }
47434     },
47435     
47436     onBlur : function()
47437     {
47438         this.beforeBlur();
47439         
47440         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47441             //this.el.removeClass(this.focusClass);
47442         }
47443         
47444         this.hasFocus = false;
47445         
47446         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47447             this.validate();
47448         }
47449         
47450         var v = this.getValue();
47451         
47452         if(String(v) !== String(this.startValue)){
47453             this.fireEvent('change', this, v, this.startValue);
47454         }
47455         
47456         this.fireEvent("blur", this);
47457     },
47458     
47459     inputEl : function()
47460     {
47461         return this.el.select('.roo-money-amount-input', true).first();
47462     },
47463     
47464     currencyEl : function()
47465     {
47466         return this.el.select('.roo-money-currency-input', true).first();
47467     },
47468     
47469     hiddenEl : function()
47470     {
47471         return this.el.select('input.hidden-number-input',true).first();
47472     }
47473     
47474 });/**
47475  * @class Roo.bootstrap.BezierSignature
47476  * @extends Roo.bootstrap.Component
47477  * Bootstrap BezierSignature class
47478  * This script refer to:
47479  *    Title: Signature Pad
47480  *    Author: szimek
47481  *    Availability: https://github.com/szimek/signature_pad
47482  *
47483  * @constructor
47484  * Create a new BezierSignature
47485  * @param {Object} config The config object
47486  */
47487
47488 Roo.bootstrap.BezierSignature = function(config){
47489     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47490     this.addEvents({
47491         "resize" : true
47492     });
47493 };
47494
47495 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47496 {
47497      
47498     curve_data: [],
47499     
47500     is_empty: true,
47501     
47502     mouse_btn_down: true,
47503     
47504     /**
47505      * @cfg {int} canvas height
47506      */
47507     canvas_height: '200px',
47508     
47509     /**
47510      * @cfg {float|function} Radius of a single dot.
47511      */ 
47512     dot_size: false,
47513     
47514     /**
47515      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47516      */
47517     min_width: 0.5,
47518     
47519     /**
47520      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47521      */
47522     max_width: 2.5,
47523     
47524     /**
47525      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47526      */
47527     throttle: 16,
47528     
47529     /**
47530      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47531      */
47532     min_distance: 5,
47533     
47534     /**
47535      * @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.
47536      */
47537     bg_color: 'rgba(0, 0, 0, 0)',
47538     
47539     /**
47540      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47541      */
47542     dot_color: 'black',
47543     
47544     /**
47545      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47546      */ 
47547     velocity_filter_weight: 0.7,
47548     
47549     /**
47550      * @cfg {function} Callback when stroke begin. 
47551      */
47552     onBegin: false,
47553     
47554     /**
47555      * @cfg {function} Callback when stroke end.
47556      */
47557     onEnd: false,
47558     
47559     getAutoCreate : function()
47560     {
47561         var cls = 'roo-signature column';
47562         
47563         if(this.cls){
47564             cls += ' ' + this.cls;
47565         }
47566         
47567         var col_sizes = [
47568             'lg',
47569             'md',
47570             'sm',
47571             'xs'
47572         ];
47573         
47574         for(var i = 0; i < col_sizes.length; i++) {
47575             if(this[col_sizes[i]]) {
47576                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47577             }
47578         }
47579         
47580         var cfg = {
47581             tag: 'div',
47582             cls: cls,
47583             cn: [
47584                 {
47585                     tag: 'div',
47586                     cls: 'roo-signature-body',
47587                     cn: [
47588                         {
47589                             tag: 'canvas',
47590                             cls: 'roo-signature-body-canvas',
47591                             height: this.canvas_height,
47592                             width: this.canvas_width
47593                         }
47594                     ]
47595                 },
47596                 {
47597                     tag: 'input',
47598                     type: 'file',
47599                     style: 'display: none'
47600                 }
47601             ]
47602         };
47603         
47604         return cfg;
47605     },
47606     
47607     initEvents: function() 
47608     {
47609         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47610         
47611         var canvas = this.canvasEl();
47612         
47613         // mouse && touch event swapping...
47614         canvas.dom.style.touchAction = 'none';
47615         canvas.dom.style.msTouchAction = 'none';
47616         
47617         this.mouse_btn_down = false;
47618         canvas.on('mousedown', this._handleMouseDown, this);
47619         canvas.on('mousemove', this._handleMouseMove, this);
47620         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47621         
47622         if (window.PointerEvent) {
47623             canvas.on('pointerdown', this._handleMouseDown, this);
47624             canvas.on('pointermove', this._handleMouseMove, this);
47625             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47626         }
47627         
47628         if ('ontouchstart' in window) {
47629             canvas.on('touchstart', this._handleTouchStart, this);
47630             canvas.on('touchmove', this._handleTouchMove, this);
47631             canvas.on('touchend', this._handleTouchEnd, this);
47632         }
47633         
47634         Roo.EventManager.onWindowResize(this.resize, this, true);
47635         
47636         // file input event
47637         this.fileEl().on('change', this.uploadImage, this);
47638         
47639         this.clear();
47640         
47641         this.resize();
47642     },
47643     
47644     resize: function(){
47645         
47646         var canvas = this.canvasEl().dom;
47647         var ctx = this.canvasElCtx();
47648         var img_data = false;
47649         
47650         if(canvas.width > 0) {
47651             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47652         }
47653         // setting canvas width will clean img data
47654         canvas.width = 0;
47655         
47656         var style = window.getComputedStyle ? 
47657             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47658             
47659         var padding_left = parseInt(style.paddingLeft) || 0;
47660         var padding_right = parseInt(style.paddingRight) || 0;
47661         
47662         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47663         
47664         if(img_data) {
47665             ctx.putImageData(img_data, 0, 0);
47666         }
47667     },
47668     
47669     _handleMouseDown: function(e)
47670     {
47671         if (e.browserEvent.which === 1) {
47672             this.mouse_btn_down = true;
47673             this.strokeBegin(e);
47674         }
47675     },
47676     
47677     _handleMouseMove: function (e)
47678     {
47679         if (this.mouse_btn_down) {
47680             this.strokeMoveUpdate(e);
47681         }
47682     },
47683     
47684     _handleMouseUp: function (e)
47685     {
47686         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47687             this.mouse_btn_down = false;
47688             this.strokeEnd(e);
47689         }
47690     },
47691     
47692     _handleTouchStart: function (e) {
47693         
47694         e.preventDefault();
47695         if (e.browserEvent.targetTouches.length === 1) {
47696             // var touch = e.browserEvent.changedTouches[0];
47697             // this.strokeBegin(touch);
47698             
47699              this.strokeBegin(e); // assume e catching the correct xy...
47700         }
47701     },
47702     
47703     _handleTouchMove: function (e) {
47704         e.preventDefault();
47705         // var touch = event.targetTouches[0];
47706         // _this._strokeMoveUpdate(touch);
47707         this.strokeMoveUpdate(e);
47708     },
47709     
47710     _handleTouchEnd: function (e) {
47711         var wasCanvasTouched = e.target === this.canvasEl().dom;
47712         if (wasCanvasTouched) {
47713             e.preventDefault();
47714             // var touch = event.changedTouches[0];
47715             // _this._strokeEnd(touch);
47716             this.strokeEnd(e);
47717         }
47718     },
47719     
47720     reset: function () {
47721         this._lastPoints = [];
47722         this._lastVelocity = 0;
47723         this._lastWidth = (this.min_width + this.max_width) / 2;
47724         this.canvasElCtx().fillStyle = this.dot_color;
47725     },
47726     
47727     strokeMoveUpdate: function(e)
47728     {
47729         this.strokeUpdate(e);
47730         
47731         if (this.throttle) {
47732             this.throttleStroke(this.strokeUpdate, this.throttle);
47733         }
47734         else {
47735             this.strokeUpdate(e);
47736         }
47737     },
47738     
47739     strokeBegin: function(e)
47740     {
47741         var newPointGroup = {
47742             color: this.dot_color,
47743             points: []
47744         };
47745         
47746         if (typeof this.onBegin === 'function') {
47747             this.onBegin(e);
47748         }
47749         
47750         this.curve_data.push(newPointGroup);
47751         this.reset();
47752         this.strokeUpdate(e);
47753     },
47754     
47755     strokeUpdate: function(e)
47756     {
47757         var rect = this.canvasEl().dom.getBoundingClientRect();
47758         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47759         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47760         var lastPoints = lastPointGroup.points;
47761         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47762         var isLastPointTooClose = lastPoint
47763             ? point.distanceTo(lastPoint) <= this.min_distance
47764             : false;
47765         var color = lastPointGroup.color;
47766         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47767             var curve = this.addPoint(point);
47768             if (!lastPoint) {
47769                 this.drawDot({color: color, point: point});
47770             }
47771             else if (curve) {
47772                 this.drawCurve({color: color, curve: curve});
47773             }
47774             lastPoints.push({
47775                 time: point.time,
47776                 x: point.x,
47777                 y: point.y
47778             });
47779         }
47780     },
47781     
47782     strokeEnd: function(e)
47783     {
47784         this.strokeUpdate(e);
47785         if (typeof this.onEnd === 'function') {
47786             this.onEnd(e);
47787         }
47788     },
47789     
47790     addPoint:  function (point) {
47791         var _lastPoints = this._lastPoints;
47792         _lastPoints.push(point);
47793         if (_lastPoints.length > 2) {
47794             if (_lastPoints.length === 3) {
47795                 _lastPoints.unshift(_lastPoints[0]);
47796             }
47797             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47798             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47799             _lastPoints.shift();
47800             return curve;
47801         }
47802         return null;
47803     },
47804     
47805     calculateCurveWidths: function (startPoint, endPoint) {
47806         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47807             (1 - this.velocity_filter_weight) * this._lastVelocity;
47808
47809         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47810         var widths = {
47811             end: newWidth,
47812             start: this._lastWidth
47813         };
47814         
47815         this._lastVelocity = velocity;
47816         this._lastWidth = newWidth;
47817         return widths;
47818     },
47819     
47820     drawDot: function (_a) {
47821         var color = _a.color, point = _a.point;
47822         var ctx = this.canvasElCtx();
47823         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47824         ctx.beginPath();
47825         this.drawCurveSegment(point.x, point.y, width);
47826         ctx.closePath();
47827         ctx.fillStyle = color;
47828         ctx.fill();
47829     },
47830     
47831     drawCurve: function (_a) {
47832         var color = _a.color, curve = _a.curve;
47833         var ctx = this.canvasElCtx();
47834         var widthDelta = curve.endWidth - curve.startWidth;
47835         var drawSteps = Math.floor(curve.length()) * 2;
47836         ctx.beginPath();
47837         ctx.fillStyle = color;
47838         for (var i = 0; i < drawSteps; i += 1) {
47839         var t = i / drawSteps;
47840         var tt = t * t;
47841         var ttt = tt * t;
47842         var u = 1 - t;
47843         var uu = u * u;
47844         var uuu = uu * u;
47845         var x = uuu * curve.startPoint.x;
47846         x += 3 * uu * t * curve.control1.x;
47847         x += 3 * u * tt * curve.control2.x;
47848         x += ttt * curve.endPoint.x;
47849         var y = uuu * curve.startPoint.y;
47850         y += 3 * uu * t * curve.control1.y;
47851         y += 3 * u * tt * curve.control2.y;
47852         y += ttt * curve.endPoint.y;
47853         var width = curve.startWidth + ttt * widthDelta;
47854         this.drawCurveSegment(x, y, width);
47855         }
47856         ctx.closePath();
47857         ctx.fill();
47858     },
47859     
47860     drawCurveSegment: function (x, y, width) {
47861         var ctx = this.canvasElCtx();
47862         ctx.moveTo(x, y);
47863         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47864         this.is_empty = false;
47865     },
47866     
47867     clear: function()
47868     {
47869         var ctx = this.canvasElCtx();
47870         var canvas = this.canvasEl().dom;
47871         ctx.fillStyle = this.bg_color;
47872         ctx.clearRect(0, 0, canvas.width, canvas.height);
47873         ctx.fillRect(0, 0, canvas.width, canvas.height);
47874         this.curve_data = [];
47875         this.reset();
47876         this.is_empty = true;
47877     },
47878     
47879     fileEl: function()
47880     {
47881         return  this.el.select('input',true).first();
47882     },
47883     
47884     canvasEl: function()
47885     {
47886         return this.el.select('canvas',true).first();
47887     },
47888     
47889     canvasElCtx: function()
47890     {
47891         return this.el.select('canvas',true).first().dom.getContext('2d');
47892     },
47893     
47894     getImage: function(type)
47895     {
47896         if(this.is_empty) {
47897             return false;
47898         }
47899         
47900         // encryption ?
47901         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47902     },
47903     
47904     drawFromImage: function(img_src)
47905     {
47906         var img = new Image();
47907         
47908         img.onload = function(){
47909             this.canvasElCtx().drawImage(img, 0, 0);
47910         }.bind(this);
47911         
47912         img.src = img_src;
47913         
47914         this.is_empty = false;
47915     },
47916     
47917     selectImage: function()
47918     {
47919         this.fileEl().dom.click();
47920     },
47921     
47922     uploadImage: function(e)
47923     {
47924         var reader = new FileReader();
47925         
47926         reader.onload = function(e){
47927             var img = new Image();
47928             img.onload = function(){
47929                 this.reset();
47930                 this.canvasElCtx().drawImage(img, 0, 0);
47931             }.bind(this);
47932             img.src = e.target.result;
47933         }.bind(this);
47934         
47935         reader.readAsDataURL(e.target.files[0]);
47936     },
47937     
47938     // Bezier Point Constructor
47939     Point: (function () {
47940         function Point(x, y, time) {
47941             this.x = x;
47942             this.y = y;
47943             this.time = time || Date.now();
47944         }
47945         Point.prototype.distanceTo = function (start) {
47946             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47947         };
47948         Point.prototype.equals = function (other) {
47949             return this.x === other.x && this.y === other.y && this.time === other.time;
47950         };
47951         Point.prototype.velocityFrom = function (start) {
47952             return this.time !== start.time
47953             ? this.distanceTo(start) / (this.time - start.time)
47954             : 0;
47955         };
47956         return Point;
47957     }()),
47958     
47959     
47960     // Bezier Constructor
47961     Bezier: (function () {
47962         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47963             this.startPoint = startPoint;
47964             this.control2 = control2;
47965             this.control1 = control1;
47966             this.endPoint = endPoint;
47967             this.startWidth = startWidth;
47968             this.endWidth = endWidth;
47969         }
47970         Bezier.fromPoints = function (points, widths, scope) {
47971             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47972             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47973             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47974         };
47975         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47976             var dx1 = s1.x - s2.x;
47977             var dy1 = s1.y - s2.y;
47978             var dx2 = s2.x - s3.x;
47979             var dy2 = s2.y - s3.y;
47980             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47981             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47982             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47983             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47984             var dxm = m1.x - m2.x;
47985             var dym = m1.y - m2.y;
47986             var k = l2 / (l1 + l2);
47987             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47988             var tx = s2.x - cm.x;
47989             var ty = s2.y - cm.y;
47990             return {
47991                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47992                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47993             };
47994         };
47995         Bezier.prototype.length = function () {
47996             var steps = 10;
47997             var length = 0;
47998             var px;
47999             var py;
48000             for (var i = 0; i <= steps; i += 1) {
48001                 var t = i / steps;
48002                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
48003                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
48004                 if (i > 0) {
48005                     var xdiff = cx - px;
48006                     var ydiff = cy - py;
48007                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48008                 }
48009                 px = cx;
48010                 py = cy;
48011             }
48012             return length;
48013         };
48014         Bezier.prototype.point = function (t, start, c1, c2, end) {
48015             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48016             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48017             + (3.0 * c2 * (1.0 - t) * t * t)
48018             + (end * t * t * t);
48019         };
48020         return Bezier;
48021     }()),
48022     
48023     throttleStroke: function(fn, wait) {
48024       if (wait === void 0) { wait = 250; }
48025       var previous = 0;
48026       var timeout = null;
48027       var result;
48028       var storedContext;
48029       var storedArgs;
48030       var later = function () {
48031           previous = Date.now();
48032           timeout = null;
48033           result = fn.apply(storedContext, storedArgs);
48034           if (!timeout) {
48035               storedContext = null;
48036               storedArgs = [];
48037           }
48038       };
48039       return function wrapper() {
48040           var args = [];
48041           for (var _i = 0; _i < arguments.length; _i++) {
48042               args[_i] = arguments[_i];
48043           }
48044           var now = Date.now();
48045           var remaining = wait - (now - previous);
48046           storedContext = this;
48047           storedArgs = args;
48048           if (remaining <= 0 || remaining > wait) {
48049               if (timeout) {
48050                   clearTimeout(timeout);
48051                   timeout = null;
48052               }
48053               previous = now;
48054               result = fn.apply(storedContext, storedArgs);
48055               if (!timeout) {
48056                   storedContext = null;
48057                   storedArgs = [];
48058               }
48059           }
48060           else if (!timeout) {
48061               timeout = window.setTimeout(later, remaining);
48062           }
48063           return result;
48064       };
48065   }
48066   
48067 });
48068
48069  
48070
48071  // old names for form elements
48072 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48073 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48074 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48075 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48076 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48077 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48078 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48079 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48080 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48081 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48082 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48083 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48084 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48085 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48086 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48087 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48088 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48089 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48090 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48091 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48092 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48093 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48094 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48095 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48096 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48097 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48098
48099 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48100 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48101
48102 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48103 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48104
48105 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48106 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48107 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48108 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48109