Roo/bootstrap/Tooltip.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.removeClass(['show', 'in']);
32864         //this.el.hide();
32865         
32866     }
32867     
32868 });
32869  
32870
32871  /*
32872  * - LGPL
32873  *
32874  * Location Picker
32875  * 
32876  */
32877
32878 /**
32879  * @class Roo.bootstrap.LocationPicker
32880  * @extends Roo.bootstrap.Component
32881  * Bootstrap LocationPicker class
32882  * @cfg {Number} latitude Position when init default 0
32883  * @cfg {Number} longitude Position when init default 0
32884  * @cfg {Number} zoom default 15
32885  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32886  * @cfg {Boolean} mapTypeControl default false
32887  * @cfg {Boolean} disableDoubleClickZoom default false
32888  * @cfg {Boolean} scrollwheel default true
32889  * @cfg {Boolean} streetViewControl default false
32890  * @cfg {Number} radius default 0
32891  * @cfg {String} locationName
32892  * @cfg {Boolean} draggable default true
32893  * @cfg {Boolean} enableAutocomplete default false
32894  * @cfg {Boolean} enableReverseGeocode default true
32895  * @cfg {String} markerTitle
32896  * 
32897  * @constructor
32898  * Create a new LocationPicker
32899  * @param {Object} config The config object
32900  */
32901
32902
32903 Roo.bootstrap.LocationPicker = function(config){
32904     
32905     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32906     
32907     this.addEvents({
32908         /**
32909          * @event initial
32910          * Fires when the picker initialized.
32911          * @param {Roo.bootstrap.LocationPicker} this
32912          * @param {Google Location} location
32913          */
32914         initial : true,
32915         /**
32916          * @event positionchanged
32917          * Fires when the picker position changed.
32918          * @param {Roo.bootstrap.LocationPicker} this
32919          * @param {Google Location} location
32920          */
32921         positionchanged : true,
32922         /**
32923          * @event resize
32924          * Fires when the map resize.
32925          * @param {Roo.bootstrap.LocationPicker} this
32926          */
32927         resize : true,
32928         /**
32929          * @event show
32930          * Fires when the map show.
32931          * @param {Roo.bootstrap.LocationPicker} this
32932          */
32933         show : true,
32934         /**
32935          * @event hide
32936          * Fires when the map hide.
32937          * @param {Roo.bootstrap.LocationPicker} this
32938          */
32939         hide : true,
32940         /**
32941          * @event mapClick
32942          * Fires when click the map.
32943          * @param {Roo.bootstrap.LocationPicker} this
32944          * @param {Map event} e
32945          */
32946         mapClick : true,
32947         /**
32948          * @event mapRightClick
32949          * Fires when right click the map.
32950          * @param {Roo.bootstrap.LocationPicker} this
32951          * @param {Map event} e
32952          */
32953         mapRightClick : true,
32954         /**
32955          * @event markerClick
32956          * Fires when click the marker.
32957          * @param {Roo.bootstrap.LocationPicker} this
32958          * @param {Map event} e
32959          */
32960         markerClick : true,
32961         /**
32962          * @event markerRightClick
32963          * Fires when right click the marker.
32964          * @param {Roo.bootstrap.LocationPicker} this
32965          * @param {Map event} e
32966          */
32967         markerRightClick : true,
32968         /**
32969          * @event OverlayViewDraw
32970          * Fires when OverlayView Draw
32971          * @param {Roo.bootstrap.LocationPicker} this
32972          */
32973         OverlayViewDraw : true,
32974         /**
32975          * @event OverlayViewOnAdd
32976          * Fires when OverlayView Draw
32977          * @param {Roo.bootstrap.LocationPicker} this
32978          */
32979         OverlayViewOnAdd : true,
32980         /**
32981          * @event OverlayViewOnRemove
32982          * Fires when OverlayView Draw
32983          * @param {Roo.bootstrap.LocationPicker} this
32984          */
32985         OverlayViewOnRemove : true,
32986         /**
32987          * @event OverlayViewShow
32988          * Fires when OverlayView Draw
32989          * @param {Roo.bootstrap.LocationPicker} this
32990          * @param {Pixel} cpx
32991          */
32992         OverlayViewShow : true,
32993         /**
32994          * @event OverlayViewHide
32995          * Fires when OverlayView Draw
32996          * @param {Roo.bootstrap.LocationPicker} this
32997          */
32998         OverlayViewHide : true,
32999         /**
33000          * @event loadexception
33001          * Fires when load google lib failed.
33002          * @param {Roo.bootstrap.LocationPicker} this
33003          */
33004         loadexception : true
33005     });
33006         
33007 };
33008
33009 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33010     
33011     gMapContext: false,
33012     
33013     latitude: 0,
33014     longitude: 0,
33015     zoom: 15,
33016     mapTypeId: false,
33017     mapTypeControl: false,
33018     disableDoubleClickZoom: false,
33019     scrollwheel: true,
33020     streetViewControl: false,
33021     radius: 0,
33022     locationName: '',
33023     draggable: true,
33024     enableAutocomplete: false,
33025     enableReverseGeocode: true,
33026     markerTitle: '',
33027     
33028     getAutoCreate: function()
33029     {
33030
33031         var cfg = {
33032             tag: 'div',
33033             cls: 'roo-location-picker'
33034         };
33035         
33036         return cfg
33037     },
33038     
33039     initEvents: function(ct, position)
33040     {       
33041         if(!this.el.getWidth() || this.isApplied()){
33042             return;
33043         }
33044         
33045         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33046         
33047         this.initial();
33048     },
33049     
33050     initial: function()
33051     {
33052         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33053             this.fireEvent('loadexception', this);
33054             return;
33055         }
33056         
33057         if(!this.mapTypeId){
33058             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33059         }
33060         
33061         this.gMapContext = this.GMapContext();
33062         
33063         this.initOverlayView();
33064         
33065         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33066         
33067         var _this = this;
33068                 
33069         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33070             _this.setPosition(_this.gMapContext.marker.position);
33071         });
33072         
33073         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33074             _this.fireEvent('mapClick', this, event);
33075             
33076         });
33077
33078         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33079             _this.fireEvent('mapRightClick', this, event);
33080             
33081         });
33082         
33083         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33084             _this.fireEvent('markerClick', this, event);
33085             
33086         });
33087
33088         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33089             _this.fireEvent('markerRightClick', this, event);
33090             
33091         });
33092         
33093         this.setPosition(this.gMapContext.location);
33094         
33095         this.fireEvent('initial', this, this.gMapContext.location);
33096     },
33097     
33098     initOverlayView: function()
33099     {
33100         var _this = this;
33101         
33102         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33103             
33104             draw: function()
33105             {
33106                 _this.fireEvent('OverlayViewDraw', _this);
33107             },
33108             
33109             onAdd: function()
33110             {
33111                 _this.fireEvent('OverlayViewOnAdd', _this);
33112             },
33113             
33114             onRemove: function()
33115             {
33116                 _this.fireEvent('OverlayViewOnRemove', _this);
33117             },
33118             
33119             show: function(cpx)
33120             {
33121                 _this.fireEvent('OverlayViewShow', _this, cpx);
33122             },
33123             
33124             hide: function()
33125             {
33126                 _this.fireEvent('OverlayViewHide', _this);
33127             }
33128             
33129         });
33130     },
33131     
33132     fromLatLngToContainerPixel: function(event)
33133     {
33134         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33135     },
33136     
33137     isApplied: function() 
33138     {
33139         return this.getGmapContext() == false ? false : true;
33140     },
33141     
33142     getGmapContext: function() 
33143     {
33144         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33145     },
33146     
33147     GMapContext: function() 
33148     {
33149         var position = new google.maps.LatLng(this.latitude, this.longitude);
33150         
33151         var _map = new google.maps.Map(this.el.dom, {
33152             center: position,
33153             zoom: this.zoom,
33154             mapTypeId: this.mapTypeId,
33155             mapTypeControl: this.mapTypeControl,
33156             disableDoubleClickZoom: this.disableDoubleClickZoom,
33157             scrollwheel: this.scrollwheel,
33158             streetViewControl: this.streetViewControl,
33159             locationName: this.locationName,
33160             draggable: this.draggable,
33161             enableAutocomplete: this.enableAutocomplete,
33162             enableReverseGeocode: this.enableReverseGeocode
33163         });
33164         
33165         var _marker = new google.maps.Marker({
33166             position: position,
33167             map: _map,
33168             title: this.markerTitle,
33169             draggable: this.draggable
33170         });
33171         
33172         return {
33173             map: _map,
33174             marker: _marker,
33175             circle: null,
33176             location: position,
33177             radius: this.radius,
33178             locationName: this.locationName,
33179             addressComponents: {
33180                 formatted_address: null,
33181                 addressLine1: null,
33182                 addressLine2: null,
33183                 streetName: null,
33184                 streetNumber: null,
33185                 city: null,
33186                 district: null,
33187                 state: null,
33188                 stateOrProvince: null
33189             },
33190             settings: this,
33191             domContainer: this.el.dom,
33192             geodecoder: new google.maps.Geocoder()
33193         };
33194     },
33195     
33196     drawCircle: function(center, radius, options) 
33197     {
33198         if (this.gMapContext.circle != null) {
33199             this.gMapContext.circle.setMap(null);
33200         }
33201         if (radius > 0) {
33202             radius *= 1;
33203             options = Roo.apply({}, options, {
33204                 strokeColor: "#0000FF",
33205                 strokeOpacity: .35,
33206                 strokeWeight: 2,
33207                 fillColor: "#0000FF",
33208                 fillOpacity: .2
33209             });
33210             
33211             options.map = this.gMapContext.map;
33212             options.radius = radius;
33213             options.center = center;
33214             this.gMapContext.circle = new google.maps.Circle(options);
33215             return this.gMapContext.circle;
33216         }
33217         
33218         return null;
33219     },
33220     
33221     setPosition: function(location) 
33222     {
33223         this.gMapContext.location = location;
33224         this.gMapContext.marker.setPosition(location);
33225         this.gMapContext.map.panTo(location);
33226         this.drawCircle(location, this.gMapContext.radius, {});
33227         
33228         var _this = this;
33229         
33230         if (this.gMapContext.settings.enableReverseGeocode) {
33231             this.gMapContext.geodecoder.geocode({
33232                 latLng: this.gMapContext.location
33233             }, function(results, status) {
33234                 
33235                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33236                     _this.gMapContext.locationName = results[0].formatted_address;
33237                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33238                     
33239                     _this.fireEvent('positionchanged', this, location);
33240                 }
33241             });
33242             
33243             return;
33244         }
33245         
33246         this.fireEvent('positionchanged', this, location);
33247     },
33248     
33249     resize: function()
33250     {
33251         google.maps.event.trigger(this.gMapContext.map, "resize");
33252         
33253         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33254         
33255         this.fireEvent('resize', this);
33256     },
33257     
33258     setPositionByLatLng: function(latitude, longitude)
33259     {
33260         this.setPosition(new google.maps.LatLng(latitude, longitude));
33261     },
33262     
33263     getCurrentPosition: function() 
33264     {
33265         return {
33266             latitude: this.gMapContext.location.lat(),
33267             longitude: this.gMapContext.location.lng()
33268         };
33269     },
33270     
33271     getAddressName: function() 
33272     {
33273         return this.gMapContext.locationName;
33274     },
33275     
33276     getAddressComponents: function() 
33277     {
33278         return this.gMapContext.addressComponents;
33279     },
33280     
33281     address_component_from_google_geocode: function(address_components) 
33282     {
33283         var result = {};
33284         
33285         for (var i = 0; i < address_components.length; i++) {
33286             var component = address_components[i];
33287             if (component.types.indexOf("postal_code") >= 0) {
33288                 result.postalCode = component.short_name;
33289             } else if (component.types.indexOf("street_number") >= 0) {
33290                 result.streetNumber = component.short_name;
33291             } else if (component.types.indexOf("route") >= 0) {
33292                 result.streetName = component.short_name;
33293             } else if (component.types.indexOf("neighborhood") >= 0) {
33294                 result.city = component.short_name;
33295             } else if (component.types.indexOf("locality") >= 0) {
33296                 result.city = component.short_name;
33297             } else if (component.types.indexOf("sublocality") >= 0) {
33298                 result.district = component.short_name;
33299             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33300                 result.stateOrProvince = component.short_name;
33301             } else if (component.types.indexOf("country") >= 0) {
33302                 result.country = component.short_name;
33303             }
33304         }
33305         
33306         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33307         result.addressLine2 = "";
33308         return result;
33309     },
33310     
33311     setZoomLevel: function(zoom)
33312     {
33313         this.gMapContext.map.setZoom(zoom);
33314     },
33315     
33316     show: function()
33317     {
33318         if(!this.el){
33319             return;
33320         }
33321         
33322         this.el.show();
33323         
33324         this.resize();
33325         
33326         this.fireEvent('show', this);
33327     },
33328     
33329     hide: function()
33330     {
33331         if(!this.el){
33332             return;
33333         }
33334         
33335         this.el.hide();
33336         
33337         this.fireEvent('hide', this);
33338     }
33339     
33340 });
33341
33342 Roo.apply(Roo.bootstrap.LocationPicker, {
33343     
33344     OverlayView : function(map, options)
33345     {
33346         options = options || {};
33347         
33348         this.setMap(map);
33349     }
33350     
33351     
33352 });/**
33353  * @class Roo.bootstrap.Alert
33354  * @extends Roo.bootstrap.Component
33355  * Bootstrap Alert class - shows an alert area box
33356  * eg
33357  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33358   Enter a valid email address
33359 </div>
33360  * @licence LGPL
33361  * @cfg {String} title The title of alert
33362  * @cfg {String} html The content of alert
33363  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33364  * @cfg {String} fa font-awesomeicon
33365  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33366  * @cfg {Boolean} close true to show a x closer
33367  * 
33368  * 
33369  * @constructor
33370  * Create a new alert
33371  * @param {Object} config The config object
33372  */
33373
33374
33375 Roo.bootstrap.Alert = function(config){
33376     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33377     
33378 };
33379
33380 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33381     
33382     title: '',
33383     html: '',
33384     weight: false,
33385     fa: false,
33386     faicon: false, // BC
33387     close : false,
33388     
33389     
33390     getAutoCreate : function()
33391     {
33392         
33393         var cfg = {
33394             tag : 'div',
33395             cls : 'alert',
33396             cn : [
33397                 {
33398                     tag: 'button',
33399                     type :  "button",
33400                     cls: "close",
33401                     html : '×',
33402                     style : this.close ? '' : 'display:none'
33403                 },
33404                 {
33405                     tag : 'i',
33406                     cls : 'roo-alert-icon'
33407                     
33408                 },
33409                 {
33410                     tag : 'b',
33411                     cls : 'roo-alert-title',
33412                     html : this.title
33413                 },
33414                 {
33415                     tag : 'span',
33416                     cls : 'roo-alert-text',
33417                     html : this.html
33418                 }
33419             ]
33420         };
33421         
33422         if(this.faicon){
33423             cfg.cn[0].cls += ' fa ' + this.faicon;
33424         }
33425         if(this.fa){
33426             cfg.cn[0].cls += ' fa ' + this.fa;
33427         }
33428         
33429         if(this.weight){
33430             cfg.cls += ' alert-' + this.weight;
33431         }
33432         
33433         return cfg;
33434     },
33435     
33436     initEvents: function() 
33437     {
33438         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33439         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33440         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33441         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33442         if (this.seconds > 0) {
33443             this.hide.defer(this.seconds, this);
33444         }
33445     },
33446     /**
33447      * Set the Title Message HTML
33448      * @param {String} html
33449      */
33450     setTitle : function(str)
33451     {
33452         this.titleEl.dom.innerHTML = str;
33453     },
33454      
33455      /**
33456      * Set the Body Message HTML
33457      * @param {String} html
33458      */
33459     setHtml : function(str)
33460     {
33461         this.htmlEl.dom.innerHTML = str;
33462     },
33463     /**
33464      * Set the Weight of the alert
33465      * @param {String} (success|info|warning|danger) weight
33466      */
33467     
33468     setWeight : function(weight)
33469     {
33470         if(this.weight){
33471             this.el.removeClass('alert-' + this.weight);
33472         }
33473         
33474         this.weight = weight;
33475         
33476         this.el.addClass('alert-' + this.weight);
33477     },
33478       /**
33479      * Set the Icon of the alert
33480      * @param {String} see fontawsome names (name without the 'fa-' bit)
33481      */
33482     setIcon : function(icon)
33483     {
33484         if(this.faicon){
33485             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33486         }
33487         
33488         this.faicon = icon;
33489         
33490         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33491     },
33492     /**
33493      * Hide the Alert
33494      */
33495     hide: function() 
33496     {
33497         this.el.hide();   
33498     },
33499     /**
33500      * Show the Alert
33501      */
33502     show: function() 
33503     {  
33504         this.el.show();   
33505     }
33506     
33507 });
33508
33509  
33510 /*
33511 * Licence: LGPL
33512 */
33513
33514 /**
33515  * @class Roo.bootstrap.UploadCropbox
33516  * @extends Roo.bootstrap.Component
33517  * Bootstrap UploadCropbox class
33518  * @cfg {String} emptyText show when image has been loaded
33519  * @cfg {String} rotateNotify show when image too small to rotate
33520  * @cfg {Number} errorTimeout default 3000
33521  * @cfg {Number} minWidth default 300
33522  * @cfg {Number} minHeight default 300
33523  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33524  * @cfg {Boolean} isDocument (true|false) default false
33525  * @cfg {String} url action url
33526  * @cfg {String} paramName default 'imageUpload'
33527  * @cfg {String} method default POST
33528  * @cfg {Boolean} loadMask (true|false) default true
33529  * @cfg {Boolean} loadingText default 'Loading...'
33530  * 
33531  * @constructor
33532  * Create a new UploadCropbox
33533  * @param {Object} config The config object
33534  */
33535
33536 Roo.bootstrap.UploadCropbox = function(config){
33537     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33538     
33539     this.addEvents({
33540         /**
33541          * @event beforeselectfile
33542          * Fire before select file
33543          * @param {Roo.bootstrap.UploadCropbox} this
33544          */
33545         "beforeselectfile" : true,
33546         /**
33547          * @event initial
33548          * Fire after initEvent
33549          * @param {Roo.bootstrap.UploadCropbox} this
33550          */
33551         "initial" : true,
33552         /**
33553          * @event crop
33554          * Fire after initEvent
33555          * @param {Roo.bootstrap.UploadCropbox} this
33556          * @param {String} data
33557          */
33558         "crop" : true,
33559         /**
33560          * @event prepare
33561          * Fire when preparing the file data
33562          * @param {Roo.bootstrap.UploadCropbox} this
33563          * @param {Object} file
33564          */
33565         "prepare" : true,
33566         /**
33567          * @event exception
33568          * Fire when get exception
33569          * @param {Roo.bootstrap.UploadCropbox} this
33570          * @param {XMLHttpRequest} xhr
33571          */
33572         "exception" : true,
33573         /**
33574          * @event beforeloadcanvas
33575          * Fire before load the canvas
33576          * @param {Roo.bootstrap.UploadCropbox} this
33577          * @param {String} src
33578          */
33579         "beforeloadcanvas" : true,
33580         /**
33581          * @event trash
33582          * Fire when trash image
33583          * @param {Roo.bootstrap.UploadCropbox} this
33584          */
33585         "trash" : true,
33586         /**
33587          * @event download
33588          * Fire when download the image
33589          * @param {Roo.bootstrap.UploadCropbox} this
33590          */
33591         "download" : true,
33592         /**
33593          * @event footerbuttonclick
33594          * Fire when footerbuttonclick
33595          * @param {Roo.bootstrap.UploadCropbox} this
33596          * @param {String} type
33597          */
33598         "footerbuttonclick" : true,
33599         /**
33600          * @event resize
33601          * Fire when resize
33602          * @param {Roo.bootstrap.UploadCropbox} this
33603          */
33604         "resize" : true,
33605         /**
33606          * @event rotate
33607          * Fire when rotate the image
33608          * @param {Roo.bootstrap.UploadCropbox} this
33609          * @param {String} pos
33610          */
33611         "rotate" : true,
33612         /**
33613          * @event inspect
33614          * Fire when inspect the file
33615          * @param {Roo.bootstrap.UploadCropbox} this
33616          * @param {Object} file
33617          */
33618         "inspect" : true,
33619         /**
33620          * @event upload
33621          * Fire when xhr upload the file
33622          * @param {Roo.bootstrap.UploadCropbox} this
33623          * @param {Object} data
33624          */
33625         "upload" : true,
33626         /**
33627          * @event arrange
33628          * Fire when arrange the file data
33629          * @param {Roo.bootstrap.UploadCropbox} this
33630          * @param {Object} formData
33631          */
33632         "arrange" : true
33633     });
33634     
33635     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33636 };
33637
33638 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33639     
33640     emptyText : 'Click to upload image',
33641     rotateNotify : 'Image is too small to rotate',
33642     errorTimeout : 3000,
33643     scale : 0,
33644     baseScale : 1,
33645     rotate : 0,
33646     dragable : false,
33647     pinching : false,
33648     mouseX : 0,
33649     mouseY : 0,
33650     cropData : false,
33651     minWidth : 300,
33652     minHeight : 300,
33653     file : false,
33654     exif : {},
33655     baseRotate : 1,
33656     cropType : 'image/jpeg',
33657     buttons : false,
33658     canvasLoaded : false,
33659     isDocument : false,
33660     method : 'POST',
33661     paramName : 'imageUpload',
33662     loadMask : true,
33663     loadingText : 'Loading...',
33664     maskEl : false,
33665     
33666     getAutoCreate : function()
33667     {
33668         var cfg = {
33669             tag : 'div',
33670             cls : 'roo-upload-cropbox',
33671             cn : [
33672                 {
33673                     tag : 'input',
33674                     cls : 'roo-upload-cropbox-selector',
33675                     type : 'file'
33676                 },
33677                 {
33678                     tag : 'div',
33679                     cls : 'roo-upload-cropbox-body',
33680                     style : 'cursor:pointer',
33681                     cn : [
33682                         {
33683                             tag : 'div',
33684                             cls : 'roo-upload-cropbox-preview'
33685                         },
33686                         {
33687                             tag : 'div',
33688                             cls : 'roo-upload-cropbox-thumb'
33689                         },
33690                         {
33691                             tag : 'div',
33692                             cls : 'roo-upload-cropbox-empty-notify',
33693                             html : this.emptyText
33694                         },
33695                         {
33696                             tag : 'div',
33697                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33698                             html : this.rotateNotify
33699                         }
33700                     ]
33701                 },
33702                 {
33703                     tag : 'div',
33704                     cls : 'roo-upload-cropbox-footer',
33705                     cn : {
33706                         tag : 'div',
33707                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33708                         cn : []
33709                     }
33710                 }
33711             ]
33712         };
33713         
33714         return cfg;
33715     },
33716     
33717     onRender : function(ct, position)
33718     {
33719         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33720         
33721         if (this.buttons.length) {
33722             
33723             Roo.each(this.buttons, function(bb) {
33724                 
33725                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33726                 
33727                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33728                 
33729             }, this);
33730         }
33731         
33732         if(this.loadMask){
33733             this.maskEl = this.el;
33734         }
33735     },
33736     
33737     initEvents : function()
33738     {
33739         this.urlAPI = (window.createObjectURL && window) || 
33740                                 (window.URL && URL.revokeObjectURL && URL) || 
33741                                 (window.webkitURL && webkitURL);
33742                         
33743         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33744         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33745         
33746         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33747         this.selectorEl.hide();
33748         
33749         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33750         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33751         
33752         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33753         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33754         this.thumbEl.hide();
33755         
33756         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33757         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33758         
33759         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33760         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33761         this.errorEl.hide();
33762         
33763         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33764         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33765         this.footerEl.hide();
33766         
33767         this.setThumbBoxSize();
33768         
33769         this.bind();
33770         
33771         this.resize();
33772         
33773         this.fireEvent('initial', this);
33774     },
33775
33776     bind : function()
33777     {
33778         var _this = this;
33779         
33780         window.addEventListener("resize", function() { _this.resize(); } );
33781         
33782         this.bodyEl.on('click', this.beforeSelectFile, this);
33783         
33784         if(Roo.isTouch){
33785             this.bodyEl.on('touchstart', this.onTouchStart, this);
33786             this.bodyEl.on('touchmove', this.onTouchMove, this);
33787             this.bodyEl.on('touchend', this.onTouchEnd, this);
33788         }
33789         
33790         if(!Roo.isTouch){
33791             this.bodyEl.on('mousedown', this.onMouseDown, this);
33792             this.bodyEl.on('mousemove', this.onMouseMove, this);
33793             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33794             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33795             Roo.get(document).on('mouseup', this.onMouseUp, this);
33796         }
33797         
33798         this.selectorEl.on('change', this.onFileSelected, this);
33799     },
33800     
33801     reset : function()
33802     {    
33803         this.scale = 0;
33804         this.baseScale = 1;
33805         this.rotate = 0;
33806         this.baseRotate = 1;
33807         this.dragable = false;
33808         this.pinching = false;
33809         this.mouseX = 0;
33810         this.mouseY = 0;
33811         this.cropData = false;
33812         this.notifyEl.dom.innerHTML = this.emptyText;
33813         
33814         this.selectorEl.dom.value = '';
33815         
33816     },
33817     
33818     resize : function()
33819     {
33820         if(this.fireEvent('resize', this) != false){
33821             this.setThumbBoxPosition();
33822             this.setCanvasPosition();
33823         }
33824     },
33825     
33826     onFooterButtonClick : function(e, el, o, type)
33827     {
33828         switch (type) {
33829             case 'rotate-left' :
33830                 this.onRotateLeft(e);
33831                 break;
33832             case 'rotate-right' :
33833                 this.onRotateRight(e);
33834                 break;
33835             case 'picture' :
33836                 this.beforeSelectFile(e);
33837                 break;
33838             case 'trash' :
33839                 this.trash(e);
33840                 break;
33841             case 'crop' :
33842                 this.crop(e);
33843                 break;
33844             case 'download' :
33845                 this.download(e);
33846                 break;
33847             default :
33848                 break;
33849         }
33850         
33851         this.fireEvent('footerbuttonclick', this, type);
33852     },
33853     
33854     beforeSelectFile : function(e)
33855     {
33856         e.preventDefault();
33857         
33858         if(this.fireEvent('beforeselectfile', this) != false){
33859             this.selectorEl.dom.click();
33860         }
33861     },
33862     
33863     onFileSelected : function(e)
33864     {
33865         e.preventDefault();
33866         
33867         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33868             return;
33869         }
33870         
33871         var file = this.selectorEl.dom.files[0];
33872         
33873         if(this.fireEvent('inspect', this, file) != false){
33874             this.prepare(file);
33875         }
33876         
33877     },
33878     
33879     trash : function(e)
33880     {
33881         this.fireEvent('trash', this);
33882     },
33883     
33884     download : function(e)
33885     {
33886         this.fireEvent('download', this);
33887     },
33888     
33889     loadCanvas : function(src)
33890     {   
33891         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33892             
33893             this.reset();
33894             
33895             this.imageEl = document.createElement('img');
33896             
33897             var _this = this;
33898             
33899             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33900             
33901             this.imageEl.src = src;
33902         }
33903     },
33904     
33905     onLoadCanvas : function()
33906     {   
33907         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33908         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33909         
33910         this.bodyEl.un('click', this.beforeSelectFile, this);
33911         
33912         this.notifyEl.hide();
33913         this.thumbEl.show();
33914         this.footerEl.show();
33915         
33916         this.baseRotateLevel();
33917         
33918         if(this.isDocument){
33919             this.setThumbBoxSize();
33920         }
33921         
33922         this.setThumbBoxPosition();
33923         
33924         this.baseScaleLevel();
33925         
33926         this.draw();
33927         
33928         this.resize();
33929         
33930         this.canvasLoaded = true;
33931         
33932         if(this.loadMask){
33933             this.maskEl.unmask();
33934         }
33935         
33936     },
33937     
33938     setCanvasPosition : function()
33939     {   
33940         if(!this.canvasEl){
33941             return;
33942         }
33943         
33944         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33945         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33946         
33947         this.previewEl.setLeft(pw);
33948         this.previewEl.setTop(ph);
33949         
33950     },
33951     
33952     onMouseDown : function(e)
33953     {   
33954         e.stopEvent();
33955         
33956         this.dragable = true;
33957         this.pinching = false;
33958         
33959         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33960             this.dragable = false;
33961             return;
33962         }
33963         
33964         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33965         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33966         
33967     },
33968     
33969     onMouseMove : function(e)
33970     {   
33971         e.stopEvent();
33972         
33973         if(!this.canvasLoaded){
33974             return;
33975         }
33976         
33977         if (!this.dragable){
33978             return;
33979         }
33980         
33981         var minX = Math.ceil(this.thumbEl.getLeft(true));
33982         var minY = Math.ceil(this.thumbEl.getTop(true));
33983         
33984         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33985         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33986         
33987         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33988         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33989         
33990         x = x - this.mouseX;
33991         y = y - this.mouseY;
33992         
33993         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33994         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33995         
33996         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33997         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33998         
33999         this.previewEl.setLeft(bgX);
34000         this.previewEl.setTop(bgY);
34001         
34002         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34003         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34004     },
34005     
34006     onMouseUp : function(e)
34007     {   
34008         e.stopEvent();
34009         
34010         this.dragable = false;
34011     },
34012     
34013     onMouseWheel : function(e)
34014     {   
34015         e.stopEvent();
34016         
34017         this.startScale = this.scale;
34018         
34019         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34020         
34021         if(!this.zoomable()){
34022             this.scale = this.startScale;
34023             return;
34024         }
34025         
34026         this.draw();
34027         
34028         return;
34029     },
34030     
34031     zoomable : function()
34032     {
34033         var minScale = this.thumbEl.getWidth() / this.minWidth;
34034         
34035         if(this.minWidth < this.minHeight){
34036             minScale = this.thumbEl.getHeight() / this.minHeight;
34037         }
34038         
34039         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34040         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34041         
34042         if(
34043                 this.isDocument &&
34044                 (this.rotate == 0 || this.rotate == 180) && 
34045                 (
34046                     width > this.imageEl.OriginWidth || 
34047                     height > this.imageEl.OriginHeight ||
34048                     (width < this.minWidth && height < this.minHeight)
34049                 )
34050         ){
34051             return false;
34052         }
34053         
34054         if(
34055                 this.isDocument &&
34056                 (this.rotate == 90 || this.rotate == 270) && 
34057                 (
34058                     width > this.imageEl.OriginWidth || 
34059                     height > this.imageEl.OriginHeight ||
34060                     (width < this.minHeight && height < this.minWidth)
34061                 )
34062         ){
34063             return false;
34064         }
34065         
34066         if(
34067                 !this.isDocument &&
34068                 (this.rotate == 0 || this.rotate == 180) && 
34069                 (
34070                     width < this.minWidth || 
34071                     width > this.imageEl.OriginWidth || 
34072                     height < this.minHeight || 
34073                     height > this.imageEl.OriginHeight
34074                 )
34075         ){
34076             return false;
34077         }
34078         
34079         if(
34080                 !this.isDocument &&
34081                 (this.rotate == 90 || this.rotate == 270) && 
34082                 (
34083                     width < this.minHeight || 
34084                     width > this.imageEl.OriginWidth || 
34085                     height < this.minWidth || 
34086                     height > this.imageEl.OriginHeight
34087                 )
34088         ){
34089             return false;
34090         }
34091         
34092         return true;
34093         
34094     },
34095     
34096     onRotateLeft : function(e)
34097     {   
34098         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34099             
34100             var minScale = this.thumbEl.getWidth() / this.minWidth;
34101             
34102             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34103             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34104             
34105             this.startScale = this.scale;
34106             
34107             while (this.getScaleLevel() < minScale){
34108             
34109                 this.scale = this.scale + 1;
34110                 
34111                 if(!this.zoomable()){
34112                     break;
34113                 }
34114                 
34115                 if(
34116                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34117                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34118                 ){
34119                     continue;
34120                 }
34121                 
34122                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34123
34124                 this.draw();
34125                 
34126                 return;
34127             }
34128             
34129             this.scale = this.startScale;
34130             
34131             this.onRotateFail();
34132             
34133             return false;
34134         }
34135         
34136         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34137
34138         if(this.isDocument){
34139             this.setThumbBoxSize();
34140             this.setThumbBoxPosition();
34141             this.setCanvasPosition();
34142         }
34143         
34144         this.draw();
34145         
34146         this.fireEvent('rotate', this, 'left');
34147         
34148     },
34149     
34150     onRotateRight : function(e)
34151     {
34152         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34153             
34154             var minScale = this.thumbEl.getWidth() / this.minWidth;
34155         
34156             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34157             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34158             
34159             this.startScale = this.scale;
34160             
34161             while (this.getScaleLevel() < minScale){
34162             
34163                 this.scale = this.scale + 1;
34164                 
34165                 if(!this.zoomable()){
34166                     break;
34167                 }
34168                 
34169                 if(
34170                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34171                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34172                 ){
34173                     continue;
34174                 }
34175                 
34176                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34177
34178                 this.draw();
34179                 
34180                 return;
34181             }
34182             
34183             this.scale = this.startScale;
34184             
34185             this.onRotateFail();
34186             
34187             return false;
34188         }
34189         
34190         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34191
34192         if(this.isDocument){
34193             this.setThumbBoxSize();
34194             this.setThumbBoxPosition();
34195             this.setCanvasPosition();
34196         }
34197         
34198         this.draw();
34199         
34200         this.fireEvent('rotate', this, 'right');
34201     },
34202     
34203     onRotateFail : function()
34204     {
34205         this.errorEl.show(true);
34206         
34207         var _this = this;
34208         
34209         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34210     },
34211     
34212     draw : function()
34213     {
34214         this.previewEl.dom.innerHTML = '';
34215         
34216         var canvasEl = document.createElement("canvas");
34217         
34218         var contextEl = canvasEl.getContext("2d");
34219         
34220         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34221         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34222         var center = this.imageEl.OriginWidth / 2;
34223         
34224         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34225             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34226             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34227             center = this.imageEl.OriginHeight / 2;
34228         }
34229         
34230         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34231         
34232         contextEl.translate(center, center);
34233         contextEl.rotate(this.rotate * Math.PI / 180);
34234
34235         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34236         
34237         this.canvasEl = document.createElement("canvas");
34238         
34239         this.contextEl = this.canvasEl.getContext("2d");
34240         
34241         switch (this.rotate) {
34242             case 0 :
34243                 
34244                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34245                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34246                 
34247                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34248                 
34249                 break;
34250             case 90 : 
34251                 
34252                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34253                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34254                 
34255                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34256                     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);
34257                     break;
34258                 }
34259                 
34260                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34261                 
34262                 break;
34263             case 180 :
34264                 
34265                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34266                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34267                 
34268                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34269                     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);
34270                     break;
34271                 }
34272                 
34273                 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);
34274                 
34275                 break;
34276             case 270 :
34277                 
34278                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34279                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34280         
34281                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34282                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34283                     break;
34284                 }
34285                 
34286                 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);
34287                 
34288                 break;
34289             default : 
34290                 break;
34291         }
34292         
34293         this.previewEl.appendChild(this.canvasEl);
34294         
34295         this.setCanvasPosition();
34296     },
34297     
34298     crop : function()
34299     {
34300         if(!this.canvasLoaded){
34301             return;
34302         }
34303         
34304         var imageCanvas = document.createElement("canvas");
34305         
34306         var imageContext = imageCanvas.getContext("2d");
34307         
34308         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34309         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34310         
34311         var center = imageCanvas.width / 2;
34312         
34313         imageContext.translate(center, center);
34314         
34315         imageContext.rotate(this.rotate * Math.PI / 180);
34316         
34317         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34318         
34319         var canvas = document.createElement("canvas");
34320         
34321         var context = canvas.getContext("2d");
34322                 
34323         canvas.width = this.minWidth;
34324         canvas.height = this.minHeight;
34325
34326         switch (this.rotate) {
34327             case 0 :
34328                 
34329                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34330                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34331                 
34332                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34333                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34334                 
34335                 var targetWidth = this.minWidth - 2 * x;
34336                 var targetHeight = this.minHeight - 2 * y;
34337                 
34338                 var scale = 1;
34339                 
34340                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34341                     scale = targetWidth / width;
34342                 }
34343                 
34344                 if(x > 0 && y == 0){
34345                     scale = targetHeight / height;
34346                 }
34347                 
34348                 if(x > 0 && y > 0){
34349                     scale = targetWidth / width;
34350                     
34351                     if(width < height){
34352                         scale = targetHeight / height;
34353                     }
34354                 }
34355                 
34356                 context.scale(scale, scale);
34357                 
34358                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34359                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34360
34361                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34362                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34363
34364                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34365                 
34366                 break;
34367             case 90 : 
34368                 
34369                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34370                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34371                 
34372                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34373                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34374                 
34375                 var targetWidth = this.minWidth - 2 * x;
34376                 var targetHeight = this.minHeight - 2 * y;
34377                 
34378                 var scale = 1;
34379                 
34380                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34381                     scale = targetWidth / width;
34382                 }
34383                 
34384                 if(x > 0 && y == 0){
34385                     scale = targetHeight / height;
34386                 }
34387                 
34388                 if(x > 0 && y > 0){
34389                     scale = targetWidth / width;
34390                     
34391                     if(width < height){
34392                         scale = targetHeight / height;
34393                     }
34394                 }
34395                 
34396                 context.scale(scale, scale);
34397                 
34398                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34399                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34400
34401                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34402                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34403                 
34404                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34405                 
34406                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34407                 
34408                 break;
34409             case 180 :
34410                 
34411                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34412                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34413                 
34414                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34415                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34416                 
34417                 var targetWidth = this.minWidth - 2 * x;
34418                 var targetHeight = this.minHeight - 2 * y;
34419                 
34420                 var scale = 1;
34421                 
34422                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34423                     scale = targetWidth / width;
34424                 }
34425                 
34426                 if(x > 0 && y == 0){
34427                     scale = targetHeight / height;
34428                 }
34429                 
34430                 if(x > 0 && y > 0){
34431                     scale = targetWidth / width;
34432                     
34433                     if(width < height){
34434                         scale = targetHeight / height;
34435                     }
34436                 }
34437                 
34438                 context.scale(scale, scale);
34439                 
34440                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34441                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34442
34443                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34444                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34445
34446                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34447                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34448                 
34449                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34450                 
34451                 break;
34452             case 270 :
34453                 
34454                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34455                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34456                 
34457                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34458                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34459                 
34460                 var targetWidth = this.minWidth - 2 * x;
34461                 var targetHeight = this.minHeight - 2 * y;
34462                 
34463                 var scale = 1;
34464                 
34465                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34466                     scale = targetWidth / width;
34467                 }
34468                 
34469                 if(x > 0 && y == 0){
34470                     scale = targetHeight / height;
34471                 }
34472                 
34473                 if(x > 0 && y > 0){
34474                     scale = targetWidth / width;
34475                     
34476                     if(width < height){
34477                         scale = targetHeight / height;
34478                     }
34479                 }
34480                 
34481                 context.scale(scale, scale);
34482                 
34483                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34484                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34485
34486                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34487                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34488                 
34489                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34490                 
34491                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34492                 
34493                 break;
34494             default : 
34495                 break;
34496         }
34497         
34498         this.cropData = canvas.toDataURL(this.cropType);
34499         
34500         if(this.fireEvent('crop', this, this.cropData) !== false){
34501             this.process(this.file, this.cropData);
34502         }
34503         
34504         return;
34505         
34506     },
34507     
34508     setThumbBoxSize : function()
34509     {
34510         var width, height;
34511         
34512         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34513             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34514             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34515             
34516             this.minWidth = width;
34517             this.minHeight = height;
34518             
34519             if(this.rotate == 90 || this.rotate == 270){
34520                 this.minWidth = height;
34521                 this.minHeight = width;
34522             }
34523         }
34524         
34525         height = 300;
34526         width = Math.ceil(this.minWidth * height / this.minHeight);
34527         
34528         if(this.minWidth > this.minHeight){
34529             width = 300;
34530             height = Math.ceil(this.minHeight * width / this.minWidth);
34531         }
34532         
34533         this.thumbEl.setStyle({
34534             width : width + 'px',
34535             height : height + 'px'
34536         });
34537
34538         return;
34539             
34540     },
34541     
34542     setThumbBoxPosition : function()
34543     {
34544         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34545         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34546         
34547         this.thumbEl.setLeft(x);
34548         this.thumbEl.setTop(y);
34549         
34550     },
34551     
34552     baseRotateLevel : function()
34553     {
34554         this.baseRotate = 1;
34555         
34556         if(
34557                 typeof(this.exif) != 'undefined' &&
34558                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34559                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34560         ){
34561             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34562         }
34563         
34564         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34565         
34566     },
34567     
34568     baseScaleLevel : function()
34569     {
34570         var width, height;
34571         
34572         if(this.isDocument){
34573             
34574             if(this.baseRotate == 6 || this.baseRotate == 8){
34575             
34576                 height = this.thumbEl.getHeight();
34577                 this.baseScale = height / this.imageEl.OriginWidth;
34578
34579                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34580                     width = this.thumbEl.getWidth();
34581                     this.baseScale = width / this.imageEl.OriginHeight;
34582                 }
34583
34584                 return;
34585             }
34586
34587             height = this.thumbEl.getHeight();
34588             this.baseScale = height / this.imageEl.OriginHeight;
34589
34590             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34591                 width = this.thumbEl.getWidth();
34592                 this.baseScale = width / this.imageEl.OriginWidth;
34593             }
34594
34595             return;
34596         }
34597         
34598         if(this.baseRotate == 6 || this.baseRotate == 8){
34599             
34600             width = this.thumbEl.getHeight();
34601             this.baseScale = width / this.imageEl.OriginHeight;
34602             
34603             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34604                 height = this.thumbEl.getWidth();
34605                 this.baseScale = height / this.imageEl.OriginHeight;
34606             }
34607             
34608             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34609                 height = this.thumbEl.getWidth();
34610                 this.baseScale = height / this.imageEl.OriginHeight;
34611                 
34612                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34613                     width = this.thumbEl.getHeight();
34614                     this.baseScale = width / this.imageEl.OriginWidth;
34615                 }
34616             }
34617             
34618             return;
34619         }
34620         
34621         width = this.thumbEl.getWidth();
34622         this.baseScale = width / this.imageEl.OriginWidth;
34623         
34624         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34625             height = this.thumbEl.getHeight();
34626             this.baseScale = height / this.imageEl.OriginHeight;
34627         }
34628         
34629         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34630             
34631             height = this.thumbEl.getHeight();
34632             this.baseScale = height / this.imageEl.OriginHeight;
34633             
34634             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34635                 width = this.thumbEl.getWidth();
34636                 this.baseScale = width / this.imageEl.OriginWidth;
34637             }
34638             
34639         }
34640         
34641         return;
34642     },
34643     
34644     getScaleLevel : function()
34645     {
34646         return this.baseScale * Math.pow(1.1, this.scale);
34647     },
34648     
34649     onTouchStart : function(e)
34650     {
34651         if(!this.canvasLoaded){
34652             this.beforeSelectFile(e);
34653             return;
34654         }
34655         
34656         var touches = e.browserEvent.touches;
34657         
34658         if(!touches){
34659             return;
34660         }
34661         
34662         if(touches.length == 1){
34663             this.onMouseDown(e);
34664             return;
34665         }
34666         
34667         if(touches.length != 2){
34668             return;
34669         }
34670         
34671         var coords = [];
34672         
34673         for(var i = 0, finger; finger = touches[i]; i++){
34674             coords.push(finger.pageX, finger.pageY);
34675         }
34676         
34677         var x = Math.pow(coords[0] - coords[2], 2);
34678         var y = Math.pow(coords[1] - coords[3], 2);
34679         
34680         this.startDistance = Math.sqrt(x + y);
34681         
34682         this.startScale = this.scale;
34683         
34684         this.pinching = true;
34685         this.dragable = false;
34686         
34687     },
34688     
34689     onTouchMove : function(e)
34690     {
34691         if(!this.pinching && !this.dragable){
34692             return;
34693         }
34694         
34695         var touches = e.browserEvent.touches;
34696         
34697         if(!touches){
34698             return;
34699         }
34700         
34701         if(this.dragable){
34702             this.onMouseMove(e);
34703             return;
34704         }
34705         
34706         var coords = [];
34707         
34708         for(var i = 0, finger; finger = touches[i]; i++){
34709             coords.push(finger.pageX, finger.pageY);
34710         }
34711         
34712         var x = Math.pow(coords[0] - coords[2], 2);
34713         var y = Math.pow(coords[1] - coords[3], 2);
34714         
34715         this.endDistance = Math.sqrt(x + y);
34716         
34717         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34718         
34719         if(!this.zoomable()){
34720             this.scale = this.startScale;
34721             return;
34722         }
34723         
34724         this.draw();
34725         
34726     },
34727     
34728     onTouchEnd : function(e)
34729     {
34730         this.pinching = false;
34731         this.dragable = false;
34732         
34733     },
34734     
34735     process : function(file, crop)
34736     {
34737         if(this.loadMask){
34738             this.maskEl.mask(this.loadingText);
34739         }
34740         
34741         this.xhr = new XMLHttpRequest();
34742         
34743         file.xhr = this.xhr;
34744
34745         this.xhr.open(this.method, this.url, true);
34746         
34747         var headers = {
34748             "Accept": "application/json",
34749             "Cache-Control": "no-cache",
34750             "X-Requested-With": "XMLHttpRequest"
34751         };
34752         
34753         for (var headerName in headers) {
34754             var headerValue = headers[headerName];
34755             if (headerValue) {
34756                 this.xhr.setRequestHeader(headerName, headerValue);
34757             }
34758         }
34759         
34760         var _this = this;
34761         
34762         this.xhr.onload = function()
34763         {
34764             _this.xhrOnLoad(_this.xhr);
34765         }
34766         
34767         this.xhr.onerror = function()
34768         {
34769             _this.xhrOnError(_this.xhr);
34770         }
34771         
34772         var formData = new FormData();
34773
34774         formData.append('returnHTML', 'NO');
34775         
34776         if(crop){
34777             formData.append('crop', crop);
34778         }
34779         
34780         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34781             formData.append(this.paramName, file, file.name);
34782         }
34783         
34784         if(typeof(file.filename) != 'undefined'){
34785             formData.append('filename', file.filename);
34786         }
34787         
34788         if(typeof(file.mimetype) != 'undefined'){
34789             formData.append('mimetype', file.mimetype);
34790         }
34791         
34792         if(this.fireEvent('arrange', this, formData) != false){
34793             this.xhr.send(formData);
34794         };
34795     },
34796     
34797     xhrOnLoad : function(xhr)
34798     {
34799         if(this.loadMask){
34800             this.maskEl.unmask();
34801         }
34802         
34803         if (xhr.readyState !== 4) {
34804             this.fireEvent('exception', this, xhr);
34805             return;
34806         }
34807
34808         var response = Roo.decode(xhr.responseText);
34809         
34810         if(!response.success){
34811             this.fireEvent('exception', this, xhr);
34812             return;
34813         }
34814         
34815         var response = Roo.decode(xhr.responseText);
34816         
34817         this.fireEvent('upload', this, response);
34818         
34819     },
34820     
34821     xhrOnError : function()
34822     {
34823         if(this.loadMask){
34824             this.maskEl.unmask();
34825         }
34826         
34827         Roo.log('xhr on error');
34828         
34829         var response = Roo.decode(xhr.responseText);
34830           
34831         Roo.log(response);
34832         
34833     },
34834     
34835     prepare : function(file)
34836     {   
34837         if(this.loadMask){
34838             this.maskEl.mask(this.loadingText);
34839         }
34840         
34841         this.file = false;
34842         this.exif = {};
34843         
34844         if(typeof(file) === 'string'){
34845             this.loadCanvas(file);
34846             return;
34847         }
34848         
34849         if(!file || !this.urlAPI){
34850             return;
34851         }
34852         
34853         this.file = file;
34854         this.cropType = file.type;
34855         
34856         var _this = this;
34857         
34858         if(this.fireEvent('prepare', this, this.file) != false){
34859             
34860             var reader = new FileReader();
34861             
34862             reader.onload = function (e) {
34863                 if (e.target.error) {
34864                     Roo.log(e.target.error);
34865                     return;
34866                 }
34867                 
34868                 var buffer = e.target.result,
34869                     dataView = new DataView(buffer),
34870                     offset = 2,
34871                     maxOffset = dataView.byteLength - 4,
34872                     markerBytes,
34873                     markerLength;
34874                 
34875                 if (dataView.getUint16(0) === 0xffd8) {
34876                     while (offset < maxOffset) {
34877                         markerBytes = dataView.getUint16(offset);
34878                         
34879                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34880                             markerLength = dataView.getUint16(offset + 2) + 2;
34881                             if (offset + markerLength > dataView.byteLength) {
34882                                 Roo.log('Invalid meta data: Invalid segment size.');
34883                                 break;
34884                             }
34885                             
34886                             if(markerBytes == 0xffe1){
34887                                 _this.parseExifData(
34888                                     dataView,
34889                                     offset,
34890                                     markerLength
34891                                 );
34892                             }
34893                             
34894                             offset += markerLength;
34895                             
34896                             continue;
34897                         }
34898                         
34899                         break;
34900                     }
34901                     
34902                 }
34903                 
34904                 var url = _this.urlAPI.createObjectURL(_this.file);
34905                 
34906                 _this.loadCanvas(url);
34907                 
34908                 return;
34909             }
34910             
34911             reader.readAsArrayBuffer(this.file);
34912             
34913         }
34914         
34915     },
34916     
34917     parseExifData : function(dataView, offset, length)
34918     {
34919         var tiffOffset = offset + 10,
34920             littleEndian,
34921             dirOffset;
34922     
34923         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34924             // No Exif data, might be XMP data instead
34925             return;
34926         }
34927         
34928         // Check for the ASCII code for "Exif" (0x45786966):
34929         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34930             // No Exif data, might be XMP data instead
34931             return;
34932         }
34933         if (tiffOffset + 8 > dataView.byteLength) {
34934             Roo.log('Invalid Exif data: Invalid segment size.');
34935             return;
34936         }
34937         // Check for the two null bytes:
34938         if (dataView.getUint16(offset + 8) !== 0x0000) {
34939             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34940             return;
34941         }
34942         // Check the byte alignment:
34943         switch (dataView.getUint16(tiffOffset)) {
34944         case 0x4949:
34945             littleEndian = true;
34946             break;
34947         case 0x4D4D:
34948             littleEndian = false;
34949             break;
34950         default:
34951             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34952             return;
34953         }
34954         // Check for the TIFF tag marker (0x002A):
34955         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34956             Roo.log('Invalid Exif data: Missing TIFF marker.');
34957             return;
34958         }
34959         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34960         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34961         
34962         this.parseExifTags(
34963             dataView,
34964             tiffOffset,
34965             tiffOffset + dirOffset,
34966             littleEndian
34967         );
34968     },
34969     
34970     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34971     {
34972         var tagsNumber,
34973             dirEndOffset,
34974             i;
34975         if (dirOffset + 6 > dataView.byteLength) {
34976             Roo.log('Invalid Exif data: Invalid directory offset.');
34977             return;
34978         }
34979         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34980         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34981         if (dirEndOffset + 4 > dataView.byteLength) {
34982             Roo.log('Invalid Exif data: Invalid directory size.');
34983             return;
34984         }
34985         for (i = 0; i < tagsNumber; i += 1) {
34986             this.parseExifTag(
34987                 dataView,
34988                 tiffOffset,
34989                 dirOffset + 2 + 12 * i, // tag offset
34990                 littleEndian
34991             );
34992         }
34993         // Return the offset to the next directory:
34994         return dataView.getUint32(dirEndOffset, littleEndian);
34995     },
34996     
34997     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34998     {
34999         var tag = dataView.getUint16(offset, littleEndian);
35000         
35001         this.exif[tag] = this.getExifValue(
35002             dataView,
35003             tiffOffset,
35004             offset,
35005             dataView.getUint16(offset + 2, littleEndian), // tag type
35006             dataView.getUint32(offset + 4, littleEndian), // tag length
35007             littleEndian
35008         );
35009     },
35010     
35011     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35012     {
35013         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35014             tagSize,
35015             dataOffset,
35016             values,
35017             i,
35018             str,
35019             c;
35020     
35021         if (!tagType) {
35022             Roo.log('Invalid Exif data: Invalid tag type.');
35023             return;
35024         }
35025         
35026         tagSize = tagType.size * length;
35027         // Determine if the value is contained in the dataOffset bytes,
35028         // or if the value at the dataOffset is a pointer to the actual data:
35029         dataOffset = tagSize > 4 ?
35030                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35031         if (dataOffset + tagSize > dataView.byteLength) {
35032             Roo.log('Invalid Exif data: Invalid data offset.');
35033             return;
35034         }
35035         if (length === 1) {
35036             return tagType.getValue(dataView, dataOffset, littleEndian);
35037         }
35038         values = [];
35039         for (i = 0; i < length; i += 1) {
35040             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35041         }
35042         
35043         if (tagType.ascii) {
35044             str = '';
35045             // Concatenate the chars:
35046             for (i = 0; i < values.length; i += 1) {
35047                 c = values[i];
35048                 // Ignore the terminating NULL byte(s):
35049                 if (c === '\u0000') {
35050                     break;
35051                 }
35052                 str += c;
35053             }
35054             return str;
35055         }
35056         return values;
35057     }
35058     
35059 });
35060
35061 Roo.apply(Roo.bootstrap.UploadCropbox, {
35062     tags : {
35063         'Orientation': 0x0112
35064     },
35065     
35066     Orientation: {
35067             1: 0, //'top-left',
35068 //            2: 'top-right',
35069             3: 180, //'bottom-right',
35070 //            4: 'bottom-left',
35071 //            5: 'left-top',
35072             6: 90, //'right-top',
35073 //            7: 'right-bottom',
35074             8: 270 //'left-bottom'
35075     },
35076     
35077     exifTagTypes : {
35078         // byte, 8-bit unsigned int:
35079         1: {
35080             getValue: function (dataView, dataOffset) {
35081                 return dataView.getUint8(dataOffset);
35082             },
35083             size: 1
35084         },
35085         // ascii, 8-bit byte:
35086         2: {
35087             getValue: function (dataView, dataOffset) {
35088                 return String.fromCharCode(dataView.getUint8(dataOffset));
35089             },
35090             size: 1,
35091             ascii: true
35092         },
35093         // short, 16 bit int:
35094         3: {
35095             getValue: function (dataView, dataOffset, littleEndian) {
35096                 return dataView.getUint16(dataOffset, littleEndian);
35097             },
35098             size: 2
35099         },
35100         // long, 32 bit int:
35101         4: {
35102             getValue: function (dataView, dataOffset, littleEndian) {
35103                 return dataView.getUint32(dataOffset, littleEndian);
35104             },
35105             size: 4
35106         },
35107         // rational = two long values, first is numerator, second is denominator:
35108         5: {
35109             getValue: function (dataView, dataOffset, littleEndian) {
35110                 return dataView.getUint32(dataOffset, littleEndian) /
35111                     dataView.getUint32(dataOffset + 4, littleEndian);
35112             },
35113             size: 8
35114         },
35115         // slong, 32 bit signed int:
35116         9: {
35117             getValue: function (dataView, dataOffset, littleEndian) {
35118                 return dataView.getInt32(dataOffset, littleEndian);
35119             },
35120             size: 4
35121         },
35122         // srational, two slongs, first is numerator, second is denominator:
35123         10: {
35124             getValue: function (dataView, dataOffset, littleEndian) {
35125                 return dataView.getInt32(dataOffset, littleEndian) /
35126                     dataView.getInt32(dataOffset + 4, littleEndian);
35127             },
35128             size: 8
35129         }
35130     },
35131     
35132     footer : {
35133         STANDARD : [
35134             {
35135                 tag : 'div',
35136                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35137                 action : 'rotate-left',
35138                 cn : [
35139                     {
35140                         tag : 'button',
35141                         cls : 'btn btn-default',
35142                         html : '<i class="fa fa-undo"></i>'
35143                     }
35144                 ]
35145             },
35146             {
35147                 tag : 'div',
35148                 cls : 'btn-group roo-upload-cropbox-picture',
35149                 action : 'picture',
35150                 cn : [
35151                     {
35152                         tag : 'button',
35153                         cls : 'btn btn-default',
35154                         html : '<i class="fa fa-picture-o"></i>'
35155                     }
35156                 ]
35157             },
35158             {
35159                 tag : 'div',
35160                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35161                 action : 'rotate-right',
35162                 cn : [
35163                     {
35164                         tag : 'button',
35165                         cls : 'btn btn-default',
35166                         html : '<i class="fa fa-repeat"></i>'
35167                     }
35168                 ]
35169             }
35170         ],
35171         DOCUMENT : [
35172             {
35173                 tag : 'div',
35174                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35175                 action : 'rotate-left',
35176                 cn : [
35177                     {
35178                         tag : 'button',
35179                         cls : 'btn btn-default',
35180                         html : '<i class="fa fa-undo"></i>'
35181                     }
35182                 ]
35183             },
35184             {
35185                 tag : 'div',
35186                 cls : 'btn-group roo-upload-cropbox-download',
35187                 action : 'download',
35188                 cn : [
35189                     {
35190                         tag : 'button',
35191                         cls : 'btn btn-default',
35192                         html : '<i class="fa fa-download"></i>'
35193                     }
35194                 ]
35195             },
35196             {
35197                 tag : 'div',
35198                 cls : 'btn-group roo-upload-cropbox-crop',
35199                 action : 'crop',
35200                 cn : [
35201                     {
35202                         tag : 'button',
35203                         cls : 'btn btn-default',
35204                         html : '<i class="fa fa-crop"></i>'
35205                     }
35206                 ]
35207             },
35208             {
35209                 tag : 'div',
35210                 cls : 'btn-group roo-upload-cropbox-trash',
35211                 action : 'trash',
35212                 cn : [
35213                     {
35214                         tag : 'button',
35215                         cls : 'btn btn-default',
35216                         html : '<i class="fa fa-trash"></i>'
35217                     }
35218                 ]
35219             },
35220             {
35221                 tag : 'div',
35222                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35223                 action : 'rotate-right',
35224                 cn : [
35225                     {
35226                         tag : 'button',
35227                         cls : 'btn btn-default',
35228                         html : '<i class="fa fa-repeat"></i>'
35229                     }
35230                 ]
35231             }
35232         ],
35233         ROTATOR : [
35234             {
35235                 tag : 'div',
35236                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35237                 action : 'rotate-left',
35238                 cn : [
35239                     {
35240                         tag : 'button',
35241                         cls : 'btn btn-default',
35242                         html : '<i class="fa fa-undo"></i>'
35243                     }
35244                 ]
35245             },
35246             {
35247                 tag : 'div',
35248                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35249                 action : 'rotate-right',
35250                 cn : [
35251                     {
35252                         tag : 'button',
35253                         cls : 'btn btn-default',
35254                         html : '<i class="fa fa-repeat"></i>'
35255                     }
35256                 ]
35257             }
35258         ]
35259     }
35260 });
35261
35262 /*
35263 * Licence: LGPL
35264 */
35265
35266 /**
35267  * @class Roo.bootstrap.DocumentManager
35268  * @extends Roo.bootstrap.Component
35269  * Bootstrap DocumentManager class
35270  * @cfg {String} paramName default 'imageUpload'
35271  * @cfg {String} toolTipName default 'filename'
35272  * @cfg {String} method default POST
35273  * @cfg {String} url action url
35274  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35275  * @cfg {Boolean} multiple multiple upload default true
35276  * @cfg {Number} thumbSize default 300
35277  * @cfg {String} fieldLabel
35278  * @cfg {Number} labelWidth default 4
35279  * @cfg {String} labelAlign (left|top) default left
35280  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35281 * @cfg {Number} labellg set the width of label (1-12)
35282  * @cfg {Number} labelmd set the width of label (1-12)
35283  * @cfg {Number} labelsm set the width of label (1-12)
35284  * @cfg {Number} labelxs set the width of label (1-12)
35285  * 
35286  * @constructor
35287  * Create a new DocumentManager
35288  * @param {Object} config The config object
35289  */
35290
35291 Roo.bootstrap.DocumentManager = function(config){
35292     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35293     
35294     this.files = [];
35295     this.delegates = [];
35296     
35297     this.addEvents({
35298         /**
35299          * @event initial
35300          * Fire when initial the DocumentManager
35301          * @param {Roo.bootstrap.DocumentManager} this
35302          */
35303         "initial" : true,
35304         /**
35305          * @event inspect
35306          * inspect selected file
35307          * @param {Roo.bootstrap.DocumentManager} this
35308          * @param {File} file
35309          */
35310         "inspect" : true,
35311         /**
35312          * @event exception
35313          * Fire when xhr load exception
35314          * @param {Roo.bootstrap.DocumentManager} this
35315          * @param {XMLHttpRequest} xhr
35316          */
35317         "exception" : true,
35318         /**
35319          * @event afterupload
35320          * Fire when xhr load exception
35321          * @param {Roo.bootstrap.DocumentManager} this
35322          * @param {XMLHttpRequest} xhr
35323          */
35324         "afterupload" : true,
35325         /**
35326          * @event prepare
35327          * prepare the form data
35328          * @param {Roo.bootstrap.DocumentManager} this
35329          * @param {Object} formData
35330          */
35331         "prepare" : true,
35332         /**
35333          * @event remove
35334          * Fire when remove the file
35335          * @param {Roo.bootstrap.DocumentManager} this
35336          * @param {Object} file
35337          */
35338         "remove" : true,
35339         /**
35340          * @event refresh
35341          * Fire after refresh the file
35342          * @param {Roo.bootstrap.DocumentManager} this
35343          */
35344         "refresh" : true,
35345         /**
35346          * @event click
35347          * Fire after click the image
35348          * @param {Roo.bootstrap.DocumentManager} this
35349          * @param {Object} file
35350          */
35351         "click" : true,
35352         /**
35353          * @event edit
35354          * Fire when upload a image and editable set to true
35355          * @param {Roo.bootstrap.DocumentManager} this
35356          * @param {Object} file
35357          */
35358         "edit" : true,
35359         /**
35360          * @event beforeselectfile
35361          * Fire before select file
35362          * @param {Roo.bootstrap.DocumentManager} this
35363          */
35364         "beforeselectfile" : true,
35365         /**
35366          * @event process
35367          * Fire before process file
35368          * @param {Roo.bootstrap.DocumentManager} this
35369          * @param {Object} file
35370          */
35371         "process" : true,
35372         /**
35373          * @event previewrendered
35374          * Fire when preview rendered
35375          * @param {Roo.bootstrap.DocumentManager} this
35376          * @param {Object} file
35377          */
35378         "previewrendered" : true,
35379         /**
35380          */
35381         "previewResize" : true
35382         
35383     });
35384 };
35385
35386 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35387     
35388     boxes : 0,
35389     inputName : '',
35390     thumbSize : 300,
35391     multiple : true,
35392     files : false,
35393     method : 'POST',
35394     url : '',
35395     paramName : 'imageUpload',
35396     toolTipName : 'filename',
35397     fieldLabel : '',
35398     labelWidth : 4,
35399     labelAlign : 'left',
35400     editable : true,
35401     delegates : false,
35402     xhr : false, 
35403     
35404     labellg : 0,
35405     labelmd : 0,
35406     labelsm : 0,
35407     labelxs : 0,
35408     
35409     getAutoCreate : function()
35410     {   
35411         var managerWidget = {
35412             tag : 'div',
35413             cls : 'roo-document-manager',
35414             cn : [
35415                 {
35416                     tag : 'input',
35417                     cls : 'roo-document-manager-selector',
35418                     type : 'file'
35419                 },
35420                 {
35421                     tag : 'div',
35422                     cls : 'roo-document-manager-uploader',
35423                     cn : [
35424                         {
35425                             tag : 'div',
35426                             cls : 'roo-document-manager-upload-btn',
35427                             html : '<i class="fa fa-plus"></i>'
35428                         }
35429                     ]
35430                     
35431                 }
35432             ]
35433         };
35434         
35435         var content = [
35436             {
35437                 tag : 'div',
35438                 cls : 'column col-md-12',
35439                 cn : managerWidget
35440             }
35441         ];
35442         
35443         if(this.fieldLabel.length){
35444             
35445             content = [
35446                 {
35447                     tag : 'div',
35448                     cls : 'column col-md-12',
35449                     html : this.fieldLabel
35450                 },
35451                 {
35452                     tag : 'div',
35453                     cls : 'column col-md-12',
35454                     cn : managerWidget
35455                 }
35456             ];
35457
35458             if(this.labelAlign == 'left'){
35459                 content = [
35460                     {
35461                         tag : 'div',
35462                         cls : 'column',
35463                         html : this.fieldLabel
35464                     },
35465                     {
35466                         tag : 'div',
35467                         cls : 'column',
35468                         cn : managerWidget
35469                     }
35470                 ];
35471                 
35472                 if(this.labelWidth > 12){
35473                     content[0].style = "width: " + this.labelWidth + 'px';
35474                 }
35475
35476                 if(this.labelWidth < 13 && this.labelmd == 0){
35477                     this.labelmd = this.labelWidth;
35478                 }
35479
35480                 if(this.labellg > 0){
35481                     content[0].cls += ' col-lg-' + this.labellg;
35482                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35483                 }
35484
35485                 if(this.labelmd > 0){
35486                     content[0].cls += ' col-md-' + this.labelmd;
35487                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35488                 }
35489
35490                 if(this.labelsm > 0){
35491                     content[0].cls += ' col-sm-' + this.labelsm;
35492                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35493                 }
35494
35495                 if(this.labelxs > 0){
35496                     content[0].cls += ' col-xs-' + this.labelxs;
35497                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35498                 }
35499                 
35500             }
35501         }
35502         
35503         var cfg = {
35504             tag : 'div',
35505             cls : 'row clearfix',
35506             cn : content
35507         };
35508         
35509         return cfg;
35510         
35511     },
35512     
35513     initEvents : function()
35514     {
35515         this.managerEl = this.el.select('.roo-document-manager', true).first();
35516         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35517         
35518         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35519         this.selectorEl.hide();
35520         
35521         if(this.multiple){
35522             this.selectorEl.attr('multiple', 'multiple');
35523         }
35524         
35525         this.selectorEl.on('change', this.onFileSelected, this);
35526         
35527         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35528         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35529         
35530         this.uploader.on('click', this.onUploaderClick, this);
35531         
35532         this.renderProgressDialog();
35533         
35534         var _this = this;
35535         
35536         window.addEventListener("resize", function() { _this.refresh(); } );
35537         
35538         this.fireEvent('initial', this);
35539     },
35540     
35541     renderProgressDialog : function()
35542     {
35543         var _this = this;
35544         
35545         this.progressDialog = new Roo.bootstrap.Modal({
35546             cls : 'roo-document-manager-progress-dialog',
35547             allow_close : false,
35548             animate : false,
35549             title : '',
35550             buttons : [
35551                 {
35552                     name  :'cancel',
35553                     weight : 'danger',
35554                     html : 'Cancel'
35555                 }
35556             ], 
35557             listeners : { 
35558                 btnclick : function() {
35559                     _this.uploadCancel();
35560                     this.hide();
35561                 }
35562             }
35563         });
35564          
35565         this.progressDialog.render(Roo.get(document.body));
35566          
35567         this.progress = new Roo.bootstrap.Progress({
35568             cls : 'roo-document-manager-progress',
35569             active : true,
35570             striped : true
35571         });
35572         
35573         this.progress.render(this.progressDialog.getChildContainer());
35574         
35575         this.progressBar = new Roo.bootstrap.ProgressBar({
35576             cls : 'roo-document-manager-progress-bar',
35577             aria_valuenow : 0,
35578             aria_valuemin : 0,
35579             aria_valuemax : 12,
35580             panel : 'success'
35581         });
35582         
35583         this.progressBar.render(this.progress.getChildContainer());
35584     },
35585     
35586     onUploaderClick : function(e)
35587     {
35588         e.preventDefault();
35589      
35590         if(this.fireEvent('beforeselectfile', this) != false){
35591             this.selectorEl.dom.click();
35592         }
35593         
35594     },
35595     
35596     onFileSelected : function(e)
35597     {
35598         e.preventDefault();
35599         
35600         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35601             return;
35602         }
35603         
35604         Roo.each(this.selectorEl.dom.files, function(file){
35605             if(this.fireEvent('inspect', this, file) != false){
35606                 this.files.push(file);
35607             }
35608         }, this);
35609         
35610         this.queue();
35611         
35612     },
35613     
35614     queue : function()
35615     {
35616         this.selectorEl.dom.value = '';
35617         
35618         if(!this.files || !this.files.length){
35619             return;
35620         }
35621         
35622         if(this.boxes > 0 && this.files.length > this.boxes){
35623             this.files = this.files.slice(0, this.boxes);
35624         }
35625         
35626         this.uploader.show();
35627         
35628         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35629             this.uploader.hide();
35630         }
35631         
35632         var _this = this;
35633         
35634         var files = [];
35635         
35636         var docs = [];
35637         
35638         Roo.each(this.files, function(file){
35639             
35640             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35641                 var f = this.renderPreview(file);
35642                 files.push(f);
35643                 return;
35644             }
35645             
35646             if(file.type.indexOf('image') != -1){
35647                 this.delegates.push(
35648                     (function(){
35649                         _this.process(file);
35650                     }).createDelegate(this)
35651                 );
35652         
35653                 return;
35654             }
35655             
35656             docs.push(
35657                 (function(){
35658                     _this.process(file);
35659                 }).createDelegate(this)
35660             );
35661             
35662         }, this);
35663         
35664         this.files = files;
35665         
35666         this.delegates = this.delegates.concat(docs);
35667         
35668         if(!this.delegates.length){
35669             this.refresh();
35670             return;
35671         }
35672         
35673         this.progressBar.aria_valuemax = this.delegates.length;
35674         
35675         this.arrange();
35676         
35677         return;
35678     },
35679     
35680     arrange : function()
35681     {
35682         if(!this.delegates.length){
35683             this.progressDialog.hide();
35684             this.refresh();
35685             return;
35686         }
35687         
35688         var delegate = this.delegates.shift();
35689         
35690         this.progressDialog.show();
35691         
35692         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35693         
35694         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35695         
35696         delegate();
35697     },
35698     
35699     refresh : function()
35700     {
35701         this.uploader.show();
35702         
35703         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35704             this.uploader.hide();
35705         }
35706         
35707         Roo.isTouch ? this.closable(false) : this.closable(true);
35708         
35709         this.fireEvent('refresh', this);
35710     },
35711     
35712     onRemove : function(e, el, o)
35713     {
35714         e.preventDefault();
35715         
35716         this.fireEvent('remove', this, o);
35717         
35718     },
35719     
35720     remove : function(o)
35721     {
35722         var files = [];
35723         
35724         Roo.each(this.files, function(file){
35725             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35726                 files.push(file);
35727                 return;
35728             }
35729
35730             o.target.remove();
35731
35732         }, this);
35733         
35734         this.files = files;
35735         
35736         this.refresh();
35737     },
35738     
35739     clear : function()
35740     {
35741         Roo.each(this.files, function(file){
35742             if(!file.target){
35743                 return;
35744             }
35745             
35746             file.target.remove();
35747
35748         }, this);
35749         
35750         this.files = [];
35751         
35752         this.refresh();
35753     },
35754     
35755     onClick : function(e, el, o)
35756     {
35757         e.preventDefault();
35758         
35759         this.fireEvent('click', this, o);
35760         
35761     },
35762     
35763     closable : function(closable)
35764     {
35765         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35766             
35767             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35768             
35769             if(closable){
35770                 el.show();
35771                 return;
35772             }
35773             
35774             el.hide();
35775             
35776         }, this);
35777     },
35778     
35779     xhrOnLoad : function(xhr)
35780     {
35781         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35782             el.remove();
35783         }, this);
35784         
35785         if (xhr.readyState !== 4) {
35786             this.arrange();
35787             this.fireEvent('exception', this, xhr);
35788             return;
35789         }
35790
35791         var response = Roo.decode(xhr.responseText);
35792         
35793         if(!response.success){
35794             this.arrange();
35795             this.fireEvent('exception', this, xhr);
35796             return;
35797         }
35798         
35799         var file = this.renderPreview(response.data);
35800         
35801         this.files.push(file);
35802         
35803         this.arrange();
35804         
35805         this.fireEvent('afterupload', this, xhr);
35806         
35807     },
35808     
35809     xhrOnError : function(xhr)
35810     {
35811         Roo.log('xhr on error');
35812         
35813         var response = Roo.decode(xhr.responseText);
35814           
35815         Roo.log(response);
35816         
35817         this.arrange();
35818     },
35819     
35820     process : function(file)
35821     {
35822         if(this.fireEvent('process', this, file) !== false){
35823             if(this.editable && file.type.indexOf('image') != -1){
35824                 this.fireEvent('edit', this, file);
35825                 return;
35826             }
35827
35828             this.uploadStart(file, false);
35829
35830             return;
35831         }
35832         
35833     },
35834     
35835     uploadStart : function(file, crop)
35836     {
35837         this.xhr = new XMLHttpRequest();
35838         
35839         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35840             this.arrange();
35841             return;
35842         }
35843         
35844         file.xhr = this.xhr;
35845             
35846         this.managerEl.createChild({
35847             tag : 'div',
35848             cls : 'roo-document-manager-loading',
35849             cn : [
35850                 {
35851                     tag : 'div',
35852                     tooltip : file.name,
35853                     cls : 'roo-document-manager-thumb',
35854                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35855                 }
35856             ]
35857
35858         });
35859
35860         this.xhr.open(this.method, this.url, true);
35861         
35862         var headers = {
35863             "Accept": "application/json",
35864             "Cache-Control": "no-cache",
35865             "X-Requested-With": "XMLHttpRequest"
35866         };
35867         
35868         for (var headerName in headers) {
35869             var headerValue = headers[headerName];
35870             if (headerValue) {
35871                 this.xhr.setRequestHeader(headerName, headerValue);
35872             }
35873         }
35874         
35875         var _this = this;
35876         
35877         this.xhr.onload = function()
35878         {
35879             _this.xhrOnLoad(_this.xhr);
35880         }
35881         
35882         this.xhr.onerror = function()
35883         {
35884             _this.xhrOnError(_this.xhr);
35885         }
35886         
35887         var formData = new FormData();
35888
35889         formData.append('returnHTML', 'NO');
35890         
35891         if(crop){
35892             formData.append('crop', crop);
35893         }
35894         
35895         formData.append(this.paramName, file, file.name);
35896         
35897         var options = {
35898             file : file, 
35899             manually : false
35900         };
35901         
35902         if(this.fireEvent('prepare', this, formData, options) != false){
35903             
35904             if(options.manually){
35905                 return;
35906             }
35907             
35908             this.xhr.send(formData);
35909             return;
35910         };
35911         
35912         this.uploadCancel();
35913     },
35914     
35915     uploadCancel : function()
35916     {
35917         if (this.xhr) {
35918             this.xhr.abort();
35919         }
35920         
35921         this.delegates = [];
35922         
35923         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35924             el.remove();
35925         }, this);
35926         
35927         this.arrange();
35928     },
35929     
35930     renderPreview : function(file)
35931     {
35932         if(typeof(file.target) != 'undefined' && file.target){
35933             return file;
35934         }
35935         
35936         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35937         
35938         var previewEl = this.managerEl.createChild({
35939             tag : 'div',
35940             cls : 'roo-document-manager-preview',
35941             cn : [
35942                 {
35943                     tag : 'div',
35944                     tooltip : file[this.toolTipName],
35945                     cls : 'roo-document-manager-thumb',
35946                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35947                 },
35948                 {
35949                     tag : 'button',
35950                     cls : 'close',
35951                     html : '<i class="fa fa-times-circle"></i>'
35952                 }
35953             ]
35954         });
35955
35956         var close = previewEl.select('button.close', true).first();
35957
35958         close.on('click', this.onRemove, this, file);
35959
35960         file.target = previewEl;
35961
35962         var image = previewEl.select('img', true).first();
35963         
35964         var _this = this;
35965         
35966         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35967         
35968         image.on('click', this.onClick, this, file);
35969         
35970         this.fireEvent('previewrendered', this, file);
35971         
35972         return file;
35973         
35974     },
35975     
35976     onPreviewLoad : function(file, image)
35977     {
35978         if(typeof(file.target) == 'undefined' || !file.target){
35979             return;
35980         }
35981         
35982         var width = image.dom.naturalWidth || image.dom.width;
35983         var height = image.dom.naturalHeight || image.dom.height;
35984         
35985         if(!this.previewResize) {
35986             return;
35987         }
35988         
35989         if(width > height){
35990             file.target.addClass('wide');
35991             return;
35992         }
35993         
35994         file.target.addClass('tall');
35995         return;
35996         
35997     },
35998     
35999     uploadFromSource : function(file, crop)
36000     {
36001         this.xhr = new XMLHttpRequest();
36002         
36003         this.managerEl.createChild({
36004             tag : 'div',
36005             cls : 'roo-document-manager-loading',
36006             cn : [
36007                 {
36008                     tag : 'div',
36009                     tooltip : file.name,
36010                     cls : 'roo-document-manager-thumb',
36011                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36012                 }
36013             ]
36014
36015         });
36016
36017         this.xhr.open(this.method, this.url, true);
36018         
36019         var headers = {
36020             "Accept": "application/json",
36021             "Cache-Control": "no-cache",
36022             "X-Requested-With": "XMLHttpRequest"
36023         };
36024         
36025         for (var headerName in headers) {
36026             var headerValue = headers[headerName];
36027             if (headerValue) {
36028                 this.xhr.setRequestHeader(headerName, headerValue);
36029             }
36030         }
36031         
36032         var _this = this;
36033         
36034         this.xhr.onload = function()
36035         {
36036             _this.xhrOnLoad(_this.xhr);
36037         }
36038         
36039         this.xhr.onerror = function()
36040         {
36041             _this.xhrOnError(_this.xhr);
36042         }
36043         
36044         var formData = new FormData();
36045
36046         formData.append('returnHTML', 'NO');
36047         
36048         formData.append('crop', crop);
36049         
36050         if(typeof(file.filename) != 'undefined'){
36051             formData.append('filename', file.filename);
36052         }
36053         
36054         if(typeof(file.mimetype) != 'undefined'){
36055             formData.append('mimetype', file.mimetype);
36056         }
36057         
36058         Roo.log(formData);
36059         
36060         if(this.fireEvent('prepare', this, formData) != false){
36061             this.xhr.send(formData);
36062         };
36063     }
36064 });
36065
36066 /*
36067 * Licence: LGPL
36068 */
36069
36070 /**
36071  * @class Roo.bootstrap.DocumentViewer
36072  * @extends Roo.bootstrap.Component
36073  * Bootstrap DocumentViewer class
36074  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36075  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36076  * 
36077  * @constructor
36078  * Create a new DocumentViewer
36079  * @param {Object} config The config object
36080  */
36081
36082 Roo.bootstrap.DocumentViewer = function(config){
36083     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36084     
36085     this.addEvents({
36086         /**
36087          * @event initial
36088          * Fire after initEvent
36089          * @param {Roo.bootstrap.DocumentViewer} this
36090          */
36091         "initial" : true,
36092         /**
36093          * @event click
36094          * Fire after click
36095          * @param {Roo.bootstrap.DocumentViewer} this
36096          */
36097         "click" : true,
36098         /**
36099          * @event download
36100          * Fire after download button
36101          * @param {Roo.bootstrap.DocumentViewer} this
36102          */
36103         "download" : true,
36104         /**
36105          * @event trash
36106          * Fire after trash button
36107          * @param {Roo.bootstrap.DocumentViewer} this
36108          */
36109         "trash" : true
36110         
36111     });
36112 };
36113
36114 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36115     
36116     showDownload : true,
36117     
36118     showTrash : true,
36119     
36120     getAutoCreate : function()
36121     {
36122         var cfg = {
36123             tag : 'div',
36124             cls : 'roo-document-viewer',
36125             cn : [
36126                 {
36127                     tag : 'div',
36128                     cls : 'roo-document-viewer-body',
36129                     cn : [
36130                         {
36131                             tag : 'div',
36132                             cls : 'roo-document-viewer-thumb',
36133                             cn : [
36134                                 {
36135                                     tag : 'img',
36136                                     cls : 'roo-document-viewer-image'
36137                                 }
36138                             ]
36139                         }
36140                     ]
36141                 },
36142                 {
36143                     tag : 'div',
36144                     cls : 'roo-document-viewer-footer',
36145                     cn : {
36146                         tag : 'div',
36147                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36148                         cn : [
36149                             {
36150                                 tag : 'div',
36151                                 cls : 'btn-group roo-document-viewer-download',
36152                                 cn : [
36153                                     {
36154                                         tag : 'button',
36155                                         cls : 'btn btn-default',
36156                                         html : '<i class="fa fa-download"></i>'
36157                                     }
36158                                 ]
36159                             },
36160                             {
36161                                 tag : 'div',
36162                                 cls : 'btn-group roo-document-viewer-trash',
36163                                 cn : [
36164                                     {
36165                                         tag : 'button',
36166                                         cls : 'btn btn-default',
36167                                         html : '<i class="fa fa-trash"></i>'
36168                                     }
36169                                 ]
36170                             }
36171                         ]
36172                     }
36173                 }
36174             ]
36175         };
36176         
36177         return cfg;
36178     },
36179     
36180     initEvents : function()
36181     {
36182         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36183         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36184         
36185         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36186         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36187         
36188         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36189         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36190         
36191         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36192         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36193         
36194         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36195         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36196         
36197         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36198         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36199         
36200         this.bodyEl.on('click', this.onClick, this);
36201         this.downloadBtn.on('click', this.onDownload, this);
36202         this.trashBtn.on('click', this.onTrash, this);
36203         
36204         this.downloadBtn.hide();
36205         this.trashBtn.hide();
36206         
36207         if(this.showDownload){
36208             this.downloadBtn.show();
36209         }
36210         
36211         if(this.showTrash){
36212             this.trashBtn.show();
36213         }
36214         
36215         if(!this.showDownload && !this.showTrash) {
36216             this.footerEl.hide();
36217         }
36218         
36219     },
36220     
36221     initial : function()
36222     {
36223         this.fireEvent('initial', this);
36224         
36225     },
36226     
36227     onClick : function(e)
36228     {
36229         e.preventDefault();
36230         
36231         this.fireEvent('click', this);
36232     },
36233     
36234     onDownload : function(e)
36235     {
36236         e.preventDefault();
36237         
36238         this.fireEvent('download', this);
36239     },
36240     
36241     onTrash : function(e)
36242     {
36243         e.preventDefault();
36244         
36245         this.fireEvent('trash', this);
36246     }
36247     
36248 });
36249 /*
36250  * - LGPL
36251  *
36252  * FieldLabel
36253  * 
36254  */
36255
36256 /**
36257  * @class Roo.bootstrap.form.FieldLabel
36258  * @extends Roo.bootstrap.Component
36259  * Bootstrap FieldLabel class
36260  * @cfg {String} html contents of the element
36261  * @cfg {String} tag tag of the element default label
36262  * @cfg {String} cls class of the element
36263  * @cfg {String} target label target 
36264  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36265  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36266  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36267  * @cfg {String} iconTooltip default "This field is required"
36268  * @cfg {String} indicatorpos (left|right) default left
36269  * 
36270  * @constructor
36271  * Create a new FieldLabel
36272  * @param {Object} config The config object
36273  */
36274
36275 Roo.bootstrap.form.FieldLabel = function(config){
36276     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36277     
36278     this.addEvents({
36279             /**
36280              * @event invalid
36281              * Fires after the field has been marked as invalid.
36282              * @param {Roo.form.FieldLabel} this
36283              * @param {String} msg The validation message
36284              */
36285             invalid : true,
36286             /**
36287              * @event valid
36288              * Fires after the field has been validated with no errors.
36289              * @param {Roo.form.FieldLabel} this
36290              */
36291             valid : true
36292         });
36293 };
36294
36295 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36296     
36297     tag: 'label',
36298     cls: '',
36299     html: '',
36300     target: '',
36301     allowBlank : true,
36302     invalidClass : 'has-warning',
36303     validClass : 'has-success',
36304     iconTooltip : 'This field is required',
36305     indicatorpos : 'left',
36306     
36307     getAutoCreate : function(){
36308         
36309         var cls = "";
36310         if (!this.allowBlank) {
36311             cls  = "visible";
36312         }
36313         
36314         var cfg = {
36315             tag : this.tag,
36316             cls : 'roo-bootstrap-field-label ' + this.cls,
36317             for : this.target,
36318             cn : [
36319                 {
36320                     tag : 'i',
36321                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36322                     tooltip : this.iconTooltip
36323                 },
36324                 {
36325                     tag : 'span',
36326                     html : this.html
36327                 }
36328             ] 
36329         };
36330         
36331         if(this.indicatorpos == 'right'){
36332             var cfg = {
36333                 tag : this.tag,
36334                 cls : 'roo-bootstrap-field-label ' + this.cls,
36335                 for : this.target,
36336                 cn : [
36337                     {
36338                         tag : 'span',
36339                         html : this.html
36340                     },
36341                     {
36342                         tag : 'i',
36343                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36344                         tooltip : this.iconTooltip
36345                     }
36346                 ] 
36347             };
36348         }
36349         
36350         return cfg;
36351     },
36352     
36353     initEvents: function() 
36354     {
36355         Roo.bootstrap.Element.superclass.initEvents.call(this);
36356         
36357         this.indicator = this.indicatorEl();
36358         
36359         if(this.indicator){
36360             this.indicator.removeClass('visible');
36361             this.indicator.addClass('invisible');
36362         }
36363         
36364         Roo.bootstrap.form.FieldLabel.register(this);
36365     },
36366     
36367     indicatorEl : function()
36368     {
36369         var indicator = this.el.select('i.roo-required-indicator',true).first();
36370         
36371         if(!indicator){
36372             return false;
36373         }
36374         
36375         return indicator;
36376         
36377     },
36378     
36379     /**
36380      * Mark this field as valid
36381      */
36382     markValid : function()
36383     {
36384         if(this.indicator){
36385             this.indicator.removeClass('visible');
36386             this.indicator.addClass('invisible');
36387         }
36388         if (Roo.bootstrap.version == 3) {
36389             this.el.removeClass(this.invalidClass);
36390             this.el.addClass(this.validClass);
36391         } else {
36392             this.el.removeClass('is-invalid');
36393             this.el.addClass('is-valid');
36394         }
36395         
36396         
36397         this.fireEvent('valid', this);
36398     },
36399     
36400     /**
36401      * Mark this field as invalid
36402      * @param {String} msg The validation message
36403      */
36404     markInvalid : function(msg)
36405     {
36406         if(this.indicator){
36407             this.indicator.removeClass('invisible');
36408             this.indicator.addClass('visible');
36409         }
36410           if (Roo.bootstrap.version == 3) {
36411             this.el.removeClass(this.validClass);
36412             this.el.addClass(this.invalidClass);
36413         } else {
36414             this.el.removeClass('is-valid');
36415             this.el.addClass('is-invalid');
36416         }
36417         
36418         
36419         this.fireEvent('invalid', this, msg);
36420     }
36421     
36422    
36423 });
36424
36425 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36426     
36427     groups: {},
36428     
36429      /**
36430     * register a FieldLabel Group
36431     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36432     */
36433     register : function(label)
36434     {
36435         if(this.groups.hasOwnProperty(label.target)){
36436             return;
36437         }
36438      
36439         this.groups[label.target] = label;
36440         
36441     },
36442     /**
36443     * fetch a FieldLabel Group based on the target
36444     * @param {string} target
36445     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36446     */
36447     get: function(target) {
36448         if (typeof(this.groups[target]) == 'undefined') {
36449             return false;
36450         }
36451         
36452         return this.groups[target] ;
36453     }
36454 });
36455
36456  
36457
36458  /*
36459  * - LGPL
36460  *
36461  * page DateSplitField.
36462  * 
36463  */
36464
36465
36466 /**
36467  * @class Roo.bootstrap.form.DateSplitField
36468  * @extends Roo.bootstrap.Component
36469  * Bootstrap DateSplitField class
36470  * @cfg {string} fieldLabel - the label associated
36471  * @cfg {Number} labelWidth set the width of label (0-12)
36472  * @cfg {String} labelAlign (top|left)
36473  * @cfg {Boolean} dayAllowBlank (true|false) default false
36474  * @cfg {Boolean} monthAllowBlank (true|false) default false
36475  * @cfg {Boolean} yearAllowBlank (true|false) default false
36476  * @cfg {string} dayPlaceholder 
36477  * @cfg {string} monthPlaceholder
36478  * @cfg {string} yearPlaceholder
36479  * @cfg {string} dayFormat default 'd'
36480  * @cfg {string} monthFormat default 'm'
36481  * @cfg {string} yearFormat default 'Y'
36482  * @cfg {Number} labellg set the width of label (1-12)
36483  * @cfg {Number} labelmd set the width of label (1-12)
36484  * @cfg {Number} labelsm set the width of label (1-12)
36485  * @cfg {Number} labelxs set the width of label (1-12)
36486
36487  *     
36488  * @constructor
36489  * Create a new DateSplitField
36490  * @param {Object} config The config object
36491  */
36492
36493 Roo.bootstrap.form.DateSplitField = function(config){
36494     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36495     
36496     this.addEvents({
36497         // raw events
36498          /**
36499          * @event years
36500          * getting the data of years
36501          * @param {Roo.bootstrap.form.DateSplitField} this
36502          * @param {Object} years
36503          */
36504         "years" : true,
36505         /**
36506          * @event days
36507          * getting the data of days
36508          * @param {Roo.bootstrap.form.DateSplitField} this
36509          * @param {Object} days
36510          */
36511         "days" : true,
36512         /**
36513          * @event invalid
36514          * Fires after the field has been marked as invalid.
36515          * @param {Roo.form.Field} this
36516          * @param {String} msg The validation message
36517          */
36518         invalid : true,
36519        /**
36520          * @event valid
36521          * Fires after the field has been validated with no errors.
36522          * @param {Roo.form.Field} this
36523          */
36524         valid : true
36525     });
36526 };
36527
36528 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36529     
36530     fieldLabel : '',
36531     labelAlign : 'top',
36532     labelWidth : 3,
36533     dayAllowBlank : false,
36534     monthAllowBlank : false,
36535     yearAllowBlank : false,
36536     dayPlaceholder : '',
36537     monthPlaceholder : '',
36538     yearPlaceholder : '',
36539     dayFormat : 'd',
36540     monthFormat : 'm',
36541     yearFormat : 'Y',
36542     isFormField : true,
36543     labellg : 0,
36544     labelmd : 0,
36545     labelsm : 0,
36546     labelxs : 0,
36547     
36548     getAutoCreate : function()
36549     {
36550         var cfg = {
36551             tag : 'div',
36552             cls : 'row roo-date-split-field-group',
36553             cn : [
36554                 {
36555                     tag : 'input',
36556                     type : 'hidden',
36557                     cls : 'form-hidden-field roo-date-split-field-group-value',
36558                     name : this.name
36559                 }
36560             ]
36561         };
36562         
36563         var labelCls = 'col-md-12';
36564         var contentCls = 'col-md-4';
36565         
36566         if(this.fieldLabel){
36567             
36568             var label = {
36569                 tag : 'div',
36570                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36571                 cn : [
36572                     {
36573                         tag : 'label',
36574                         html : this.fieldLabel
36575                     }
36576                 ]
36577             };
36578             
36579             if(this.labelAlign == 'left'){
36580             
36581                 if(this.labelWidth > 12){
36582                     label.style = "width: " + this.labelWidth + 'px';
36583                 }
36584
36585                 if(this.labelWidth < 13 && this.labelmd == 0){
36586                     this.labelmd = this.labelWidth;
36587                 }
36588
36589                 if(this.labellg > 0){
36590                     labelCls = ' col-lg-' + this.labellg;
36591                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36592                 }
36593
36594                 if(this.labelmd > 0){
36595                     labelCls = ' col-md-' + this.labelmd;
36596                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36597                 }
36598
36599                 if(this.labelsm > 0){
36600                     labelCls = ' col-sm-' + this.labelsm;
36601                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36602                 }
36603
36604                 if(this.labelxs > 0){
36605                     labelCls = ' col-xs-' + this.labelxs;
36606                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36607                 }
36608             }
36609             
36610             label.cls += ' ' + labelCls;
36611             
36612             cfg.cn.push(label);
36613         }
36614         
36615         Roo.each(['day', 'month', 'year'], function(t){
36616             cfg.cn.push({
36617                 tag : 'div',
36618                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36619             });
36620         }, this);
36621         
36622         return cfg;
36623     },
36624     
36625     inputEl: function ()
36626     {
36627         return this.el.select('.roo-date-split-field-group-value', true).first();
36628     },
36629     
36630     onRender : function(ct, position) 
36631     {
36632         var _this = this;
36633         
36634         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36635         
36636         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36637         
36638         this.dayField = new Roo.bootstrap.form.ComboBox({
36639             allowBlank : this.dayAllowBlank,
36640             alwaysQuery : true,
36641             displayField : 'value',
36642             editable : false,
36643             fieldLabel : '',
36644             forceSelection : true,
36645             mode : 'local',
36646             placeholder : this.dayPlaceholder,
36647             selectOnFocus : true,
36648             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36649             triggerAction : 'all',
36650             typeAhead : true,
36651             valueField : 'value',
36652             store : new Roo.data.SimpleStore({
36653                 data : (function() {    
36654                     var days = [];
36655                     _this.fireEvent('days', _this, days);
36656                     return days;
36657                 })(),
36658                 fields : [ 'value' ]
36659             }),
36660             listeners : {
36661                 select : function (_self, record, index)
36662                 {
36663                     _this.setValue(_this.getValue());
36664                 }
36665             }
36666         });
36667
36668         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36669         
36670         this.monthField = new Roo.bootstrap.form.MonthField({
36671             after : '<i class=\"fa fa-calendar\"></i>',
36672             allowBlank : this.monthAllowBlank,
36673             placeholder : this.monthPlaceholder,
36674             readOnly : true,
36675             listeners : {
36676                 render : function (_self)
36677                 {
36678                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36679                         e.preventDefault();
36680                         _self.focus();
36681                     });
36682                 },
36683                 select : function (_self, oldvalue, newvalue)
36684                 {
36685                     _this.setValue(_this.getValue());
36686                 }
36687             }
36688         });
36689         
36690         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36691         
36692         this.yearField = new Roo.bootstrap.form.ComboBox({
36693             allowBlank : this.yearAllowBlank,
36694             alwaysQuery : true,
36695             displayField : 'value',
36696             editable : false,
36697             fieldLabel : '',
36698             forceSelection : true,
36699             mode : 'local',
36700             placeholder : this.yearPlaceholder,
36701             selectOnFocus : true,
36702             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36703             triggerAction : 'all',
36704             typeAhead : true,
36705             valueField : 'value',
36706             store : new Roo.data.SimpleStore({
36707                 data : (function() {
36708                     var years = [];
36709                     _this.fireEvent('years', _this, years);
36710                     return years;
36711                 })(),
36712                 fields : [ 'value' ]
36713             }),
36714             listeners : {
36715                 select : function (_self, record, index)
36716                 {
36717                     _this.setValue(_this.getValue());
36718                 }
36719             }
36720         });
36721
36722         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36723     },
36724     
36725     setValue : function(v, format)
36726     {
36727         this.inputEl.dom.value = v;
36728         
36729         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36730         
36731         var d = Date.parseDate(v, f);
36732         
36733         if(!d){
36734             this.validate();
36735             return;
36736         }
36737         
36738         this.setDay(d.format(this.dayFormat));
36739         this.setMonth(d.format(this.monthFormat));
36740         this.setYear(d.format(this.yearFormat));
36741         
36742         this.validate();
36743         
36744         return;
36745     },
36746     
36747     setDay : function(v)
36748     {
36749         this.dayField.setValue(v);
36750         this.inputEl.dom.value = this.getValue();
36751         this.validate();
36752         return;
36753     },
36754     
36755     setMonth : function(v)
36756     {
36757         this.monthField.setValue(v, true);
36758         this.inputEl.dom.value = this.getValue();
36759         this.validate();
36760         return;
36761     },
36762     
36763     setYear : function(v)
36764     {
36765         this.yearField.setValue(v);
36766         this.inputEl.dom.value = this.getValue();
36767         this.validate();
36768         return;
36769     },
36770     
36771     getDay : function()
36772     {
36773         return this.dayField.getValue();
36774     },
36775     
36776     getMonth : function()
36777     {
36778         return this.monthField.getValue();
36779     },
36780     
36781     getYear : function()
36782     {
36783         return this.yearField.getValue();
36784     },
36785     
36786     getValue : function()
36787     {
36788         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36789         
36790         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36791         
36792         return date;
36793     },
36794     
36795     reset : function()
36796     {
36797         this.setDay('');
36798         this.setMonth('');
36799         this.setYear('');
36800         this.inputEl.dom.value = '';
36801         this.validate();
36802         return;
36803     },
36804     
36805     validate : function()
36806     {
36807         var d = this.dayField.validate();
36808         var m = this.monthField.validate();
36809         var y = this.yearField.validate();
36810         
36811         var valid = true;
36812         
36813         if(
36814                 (!this.dayAllowBlank && !d) ||
36815                 (!this.monthAllowBlank && !m) ||
36816                 (!this.yearAllowBlank && !y)
36817         ){
36818             valid = false;
36819         }
36820         
36821         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36822             return valid;
36823         }
36824         
36825         if(valid){
36826             this.markValid();
36827             return valid;
36828         }
36829         
36830         this.markInvalid();
36831         
36832         return valid;
36833     },
36834     
36835     markValid : function()
36836     {
36837         
36838         var label = this.el.select('label', true).first();
36839         var icon = this.el.select('i.fa-star', true).first();
36840
36841         if(label && icon){
36842             icon.remove();
36843         }
36844         
36845         this.fireEvent('valid', this);
36846     },
36847     
36848      /**
36849      * Mark this field as invalid
36850      * @param {String} msg The validation message
36851      */
36852     markInvalid : function(msg)
36853     {
36854         
36855         var label = this.el.select('label', true).first();
36856         var icon = this.el.select('i.fa-star', true).first();
36857
36858         if(label && !icon){
36859             this.el.select('.roo-date-split-field-label', true).createChild({
36860                 tag : 'i',
36861                 cls : 'text-danger fa fa-lg fa-star',
36862                 tooltip : 'This field is required',
36863                 style : 'margin-right:5px;'
36864             }, label, true);
36865         }
36866         
36867         this.fireEvent('invalid', this, msg);
36868     },
36869     
36870     clearInvalid : function()
36871     {
36872         var label = this.el.select('label', true).first();
36873         var icon = this.el.select('i.fa-star', true).first();
36874
36875         if(label && icon){
36876             icon.remove();
36877         }
36878         
36879         this.fireEvent('valid', this);
36880     },
36881     
36882     getName: function()
36883     {
36884         return this.name;
36885     }
36886     
36887 });
36888
36889  
36890
36891 /**
36892  * @class Roo.bootstrap.LayoutMasonry
36893  * @extends Roo.bootstrap.Component
36894  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36895  * Bootstrap Layout Masonry class
36896  *
36897  * This is based on 
36898  * http://masonry.desandro.com
36899  *
36900  * The idea is to render all the bricks based on vertical width...
36901  *
36902  * The original code extends 'outlayer' - we might need to use that....
36903
36904  * @constructor
36905  * Create a new Element
36906  * @param {Object} config The config object
36907  */
36908
36909 Roo.bootstrap.LayoutMasonry = function(config){
36910     
36911     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36912     
36913     this.bricks = [];
36914     
36915     Roo.bootstrap.LayoutMasonry.register(this);
36916     
36917     this.addEvents({
36918         // raw events
36919         /**
36920          * @event layout
36921          * Fire after layout the items
36922          * @param {Roo.bootstrap.LayoutMasonry} this
36923          * @param {Roo.EventObject} e
36924          */
36925         "layout" : true
36926     });
36927     
36928 };
36929
36930 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36931     
36932     /**
36933      * @cfg {Boolean} isLayoutInstant = no animation?
36934      */   
36935     isLayoutInstant : false, // needed?
36936    
36937     /**
36938      * @cfg {Number} boxWidth  width of the columns
36939      */   
36940     boxWidth : 450,
36941     
36942       /**
36943      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36944      */   
36945     boxHeight : 0,
36946     
36947     /**
36948      * @cfg {Number} padWidth padding below box..
36949      */   
36950     padWidth : 10, 
36951     
36952     /**
36953      * @cfg {Number} gutter gutter width..
36954      */   
36955     gutter : 10,
36956     
36957      /**
36958      * @cfg {Number} maxCols maximum number of columns
36959      */   
36960     
36961     maxCols: 0,
36962     
36963     /**
36964      * @cfg {Boolean} isAutoInitial defalut true
36965      */   
36966     isAutoInitial : true, 
36967     
36968     containerWidth: 0,
36969     
36970     /**
36971      * @cfg {Boolean} isHorizontal defalut false
36972      */   
36973     isHorizontal : false, 
36974
36975     currentSize : null,
36976     
36977     tag: 'div',
36978     
36979     cls: '',
36980     
36981     bricks: null, //CompositeElement
36982     
36983     cols : 1,
36984     
36985     _isLayoutInited : false,
36986     
36987 //    isAlternative : false, // only use for vertical layout...
36988     
36989     /**
36990      * @cfg {Number} alternativePadWidth padding below box..
36991      */   
36992     alternativePadWidth : 50,
36993     
36994     selectedBrick : [],
36995     
36996     getAutoCreate : function(){
36997         
36998         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36999         
37000         var cfg = {
37001             tag: this.tag,
37002             cls: 'blog-masonary-wrapper ' + this.cls,
37003             cn : {
37004                 cls : 'mas-boxes masonary'
37005             }
37006         };
37007         
37008         return cfg;
37009     },
37010     
37011     getChildContainer: function( )
37012     {
37013         if (this.boxesEl) {
37014             return this.boxesEl;
37015         }
37016         
37017         this.boxesEl = this.el.select('.mas-boxes').first();
37018         
37019         return this.boxesEl;
37020     },
37021     
37022     
37023     initEvents : function()
37024     {
37025         var _this = this;
37026         
37027         if(this.isAutoInitial){
37028             Roo.log('hook children rendered');
37029             this.on('childrenrendered', function() {
37030                 Roo.log('children rendered');
37031                 _this.initial();
37032             } ,this);
37033         }
37034     },
37035     
37036     initial : function()
37037     {
37038         this.selectedBrick = [];
37039         
37040         this.currentSize = this.el.getBox(true);
37041         
37042         Roo.EventManager.onWindowResize(this.resize, this); 
37043
37044         if(!this.isAutoInitial){
37045             this.layout();
37046             return;
37047         }
37048         
37049         this.layout();
37050         
37051         return;
37052         //this.layout.defer(500,this);
37053         
37054     },
37055     
37056     resize : function()
37057     {
37058         var cs = this.el.getBox(true);
37059         
37060         if (
37061                 this.currentSize.width == cs.width && 
37062                 this.currentSize.x == cs.x && 
37063                 this.currentSize.height == cs.height && 
37064                 this.currentSize.y == cs.y 
37065         ) {
37066             Roo.log("no change in with or X or Y");
37067             return;
37068         }
37069         
37070         this.currentSize = cs;
37071         
37072         this.layout();
37073         
37074     },
37075     
37076     layout : function()
37077     {   
37078         this._resetLayout();
37079         
37080         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37081         
37082         this.layoutItems( isInstant );
37083       
37084         this._isLayoutInited = true;
37085         
37086         this.fireEvent('layout', this);
37087         
37088     },
37089     
37090     _resetLayout : function()
37091     {
37092         if(this.isHorizontal){
37093             this.horizontalMeasureColumns();
37094             return;
37095         }
37096         
37097         this.verticalMeasureColumns();
37098         
37099     },
37100     
37101     verticalMeasureColumns : function()
37102     {
37103         this.getContainerWidth();
37104         
37105 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37106 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37107 //            return;
37108 //        }
37109         
37110         var boxWidth = this.boxWidth + this.padWidth;
37111         
37112         if(this.containerWidth < this.boxWidth){
37113             boxWidth = this.containerWidth
37114         }
37115         
37116         var containerWidth = this.containerWidth;
37117         
37118         var cols = Math.floor(containerWidth / boxWidth);
37119         
37120         this.cols = Math.max( cols, 1 );
37121         
37122         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37123         
37124         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37125         
37126         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37127         
37128         this.colWidth = boxWidth + avail - this.padWidth;
37129         
37130         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37131         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37132     },
37133     
37134     horizontalMeasureColumns : function()
37135     {
37136         this.getContainerWidth();
37137         
37138         var boxWidth = this.boxWidth;
37139         
37140         if(this.containerWidth < boxWidth){
37141             boxWidth = this.containerWidth;
37142         }
37143         
37144         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37145         
37146         this.el.setHeight(boxWidth);
37147         
37148     },
37149     
37150     getContainerWidth : function()
37151     {
37152         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37153     },
37154     
37155     layoutItems : function( isInstant )
37156     {
37157         Roo.log(this.bricks);
37158         
37159         var items = Roo.apply([], this.bricks);
37160         
37161         if(this.isHorizontal){
37162             this._horizontalLayoutItems( items , isInstant );
37163             return;
37164         }
37165         
37166 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37167 //            this._verticalAlternativeLayoutItems( items , isInstant );
37168 //            return;
37169 //        }
37170         
37171         this._verticalLayoutItems( items , isInstant );
37172         
37173     },
37174     
37175     _verticalLayoutItems : function ( items , isInstant)
37176     {
37177         if ( !items || !items.length ) {
37178             return;
37179         }
37180         
37181         var standard = [
37182             ['xs', 'xs', 'xs', 'tall'],
37183             ['xs', 'xs', 'tall'],
37184             ['xs', 'xs', 'sm'],
37185             ['xs', 'xs', 'xs'],
37186             ['xs', 'tall'],
37187             ['xs', 'sm'],
37188             ['xs', 'xs'],
37189             ['xs'],
37190             
37191             ['sm', 'xs', 'xs'],
37192             ['sm', 'xs'],
37193             ['sm'],
37194             
37195             ['tall', 'xs', 'xs', 'xs'],
37196             ['tall', 'xs', 'xs'],
37197             ['tall', 'xs'],
37198             ['tall']
37199             
37200         ];
37201         
37202         var queue = [];
37203         
37204         var boxes = [];
37205         
37206         var box = [];
37207         
37208         Roo.each(items, function(item, k){
37209             
37210             switch (item.size) {
37211                 // these layouts take up a full box,
37212                 case 'md' :
37213                 case 'md-left' :
37214                 case 'md-right' :
37215                 case 'wide' :
37216                     
37217                     if(box.length){
37218                         boxes.push(box);
37219                         box = [];
37220                     }
37221                     
37222                     boxes.push([item]);
37223                     
37224                     break;
37225                     
37226                 case 'xs' :
37227                 case 'sm' :
37228                 case 'tall' :
37229                     
37230                     box.push(item);
37231                     
37232                     break;
37233                 default :
37234                     break;
37235                     
37236             }
37237             
37238         }, this);
37239         
37240         if(box.length){
37241             boxes.push(box);
37242             box = [];
37243         }
37244         
37245         var filterPattern = function(box, length)
37246         {
37247             if(!box.length){
37248                 return;
37249             }
37250             
37251             var match = false;
37252             
37253             var pattern = box.slice(0, length);
37254             
37255             var format = [];
37256             
37257             Roo.each(pattern, function(i){
37258                 format.push(i.size);
37259             }, this);
37260             
37261             Roo.each(standard, function(s){
37262                 
37263                 if(String(s) != String(format)){
37264                     return;
37265                 }
37266                 
37267                 match = true;
37268                 return false;
37269                 
37270             }, this);
37271             
37272             if(!match && length == 1){
37273                 return;
37274             }
37275             
37276             if(!match){
37277                 filterPattern(box, length - 1);
37278                 return;
37279             }
37280                 
37281             queue.push(pattern);
37282
37283             box = box.slice(length, box.length);
37284
37285             filterPattern(box, 4);
37286
37287             return;
37288             
37289         }
37290         
37291         Roo.each(boxes, function(box, k){
37292             
37293             if(!box.length){
37294                 return;
37295             }
37296             
37297             if(box.length == 1){
37298                 queue.push(box);
37299                 return;
37300             }
37301             
37302             filterPattern(box, 4);
37303             
37304         }, this);
37305         
37306         this._processVerticalLayoutQueue( queue, isInstant );
37307         
37308     },
37309     
37310 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37311 //    {
37312 //        if ( !items || !items.length ) {
37313 //            return;
37314 //        }
37315 //
37316 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37317 //        
37318 //    },
37319     
37320     _horizontalLayoutItems : function ( items , isInstant)
37321     {
37322         if ( !items || !items.length || items.length < 3) {
37323             return;
37324         }
37325         
37326         items.reverse();
37327         
37328         var eItems = items.slice(0, 3);
37329         
37330         items = items.slice(3, items.length);
37331         
37332         var standard = [
37333             ['xs', 'xs', 'xs', 'wide'],
37334             ['xs', 'xs', 'wide'],
37335             ['xs', 'xs', 'sm'],
37336             ['xs', 'xs', 'xs'],
37337             ['xs', 'wide'],
37338             ['xs', 'sm'],
37339             ['xs', 'xs'],
37340             ['xs'],
37341             
37342             ['sm', 'xs', 'xs'],
37343             ['sm', 'xs'],
37344             ['sm'],
37345             
37346             ['wide', 'xs', 'xs', 'xs'],
37347             ['wide', 'xs', 'xs'],
37348             ['wide', 'xs'],
37349             ['wide'],
37350             
37351             ['wide-thin']
37352         ];
37353         
37354         var queue = [];
37355         
37356         var boxes = [];
37357         
37358         var box = [];
37359         
37360         Roo.each(items, function(item, k){
37361             
37362             switch (item.size) {
37363                 case 'md' :
37364                 case 'md-left' :
37365                 case 'md-right' :
37366                 case 'tall' :
37367                     
37368                     if(box.length){
37369                         boxes.push(box);
37370                         box = [];
37371                     }
37372                     
37373                     boxes.push([item]);
37374                     
37375                     break;
37376                     
37377                 case 'xs' :
37378                 case 'sm' :
37379                 case 'wide' :
37380                 case 'wide-thin' :
37381                     
37382                     box.push(item);
37383                     
37384                     break;
37385                 default :
37386                     break;
37387                     
37388             }
37389             
37390         }, this);
37391         
37392         if(box.length){
37393             boxes.push(box);
37394             box = [];
37395         }
37396         
37397         var filterPattern = function(box, length)
37398         {
37399             if(!box.length){
37400                 return;
37401             }
37402             
37403             var match = false;
37404             
37405             var pattern = box.slice(0, length);
37406             
37407             var format = [];
37408             
37409             Roo.each(pattern, function(i){
37410                 format.push(i.size);
37411             }, this);
37412             
37413             Roo.each(standard, function(s){
37414                 
37415                 if(String(s) != String(format)){
37416                     return;
37417                 }
37418                 
37419                 match = true;
37420                 return false;
37421                 
37422             }, this);
37423             
37424             if(!match && length == 1){
37425                 return;
37426             }
37427             
37428             if(!match){
37429                 filterPattern(box, length - 1);
37430                 return;
37431             }
37432                 
37433             queue.push(pattern);
37434
37435             box = box.slice(length, box.length);
37436
37437             filterPattern(box, 4);
37438
37439             return;
37440             
37441         }
37442         
37443         Roo.each(boxes, function(box, k){
37444             
37445             if(!box.length){
37446                 return;
37447             }
37448             
37449             if(box.length == 1){
37450                 queue.push(box);
37451                 return;
37452             }
37453             
37454             filterPattern(box, 4);
37455             
37456         }, this);
37457         
37458         
37459         var prune = [];
37460         
37461         var pos = this.el.getBox(true);
37462         
37463         var minX = pos.x;
37464         
37465         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37466         
37467         var hit_end = false;
37468         
37469         Roo.each(queue, function(box){
37470             
37471             if(hit_end){
37472                 
37473                 Roo.each(box, function(b){
37474                 
37475                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37476                     b.el.hide();
37477
37478                 }, this);
37479
37480                 return;
37481             }
37482             
37483             var mx = 0;
37484             
37485             Roo.each(box, function(b){
37486                 
37487                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37488                 b.el.show();
37489
37490                 mx = Math.max(mx, b.x);
37491                 
37492             }, this);
37493             
37494             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37495             
37496             if(maxX < minX){
37497                 
37498                 Roo.each(box, function(b){
37499                 
37500                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37501                     b.el.hide();
37502                     
37503                 }, this);
37504                 
37505                 hit_end = true;
37506                 
37507                 return;
37508             }
37509             
37510             prune.push(box);
37511             
37512         }, this);
37513         
37514         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37515     },
37516     
37517     /** Sets position of item in DOM
37518     * @param {Element} item
37519     * @param {Number} x - horizontal position
37520     * @param {Number} y - vertical position
37521     * @param {Boolean} isInstant - disables transitions
37522     */
37523     _processVerticalLayoutQueue : function( queue, isInstant )
37524     {
37525         var pos = this.el.getBox(true);
37526         var x = pos.x;
37527         var y = pos.y;
37528         var maxY = [];
37529         
37530         for (var i = 0; i < this.cols; i++){
37531             maxY[i] = pos.y;
37532         }
37533         
37534         Roo.each(queue, function(box, k){
37535             
37536             var col = k % this.cols;
37537             
37538             Roo.each(box, function(b,kk){
37539                 
37540                 b.el.position('absolute');
37541                 
37542                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37543                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37544                 
37545                 if(b.size == 'md-left' || b.size == 'md-right'){
37546                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37547                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37548                 }
37549                 
37550                 b.el.setWidth(width);
37551                 b.el.setHeight(height);
37552                 // iframe?
37553                 b.el.select('iframe',true).setSize(width,height);
37554                 
37555             }, this);
37556             
37557             for (var i = 0; i < this.cols; i++){
37558                 
37559                 if(maxY[i] < maxY[col]){
37560                     col = i;
37561                     continue;
37562                 }
37563                 
37564                 col = Math.min(col, i);
37565                 
37566             }
37567             
37568             x = pos.x + col * (this.colWidth + this.padWidth);
37569             
37570             y = maxY[col];
37571             
37572             var positions = [];
37573             
37574             switch (box.length){
37575                 case 1 :
37576                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37577                     break;
37578                 case 2 :
37579                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37580                     break;
37581                 case 3 :
37582                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37583                     break;
37584                 case 4 :
37585                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37586                     break;
37587                 default :
37588                     break;
37589             }
37590             
37591             Roo.each(box, function(b,kk){
37592                 
37593                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37594                 
37595                 var sz = b.el.getSize();
37596                 
37597                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37598                 
37599             }, this);
37600             
37601         }, this);
37602         
37603         var mY = 0;
37604         
37605         for (var i = 0; i < this.cols; i++){
37606             mY = Math.max(mY, maxY[i]);
37607         }
37608         
37609         this.el.setHeight(mY - pos.y);
37610         
37611     },
37612     
37613 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37614 //    {
37615 //        var pos = this.el.getBox(true);
37616 //        var x = pos.x;
37617 //        var y = pos.y;
37618 //        var maxX = pos.right;
37619 //        
37620 //        var maxHeight = 0;
37621 //        
37622 //        Roo.each(items, function(item, k){
37623 //            
37624 //            var c = k % 2;
37625 //            
37626 //            item.el.position('absolute');
37627 //                
37628 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37629 //
37630 //            item.el.setWidth(width);
37631 //
37632 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37633 //
37634 //            item.el.setHeight(height);
37635 //            
37636 //            if(c == 0){
37637 //                item.el.setXY([x, y], isInstant ? false : true);
37638 //            } else {
37639 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37640 //            }
37641 //            
37642 //            y = y + height + this.alternativePadWidth;
37643 //            
37644 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37645 //            
37646 //        }, this);
37647 //        
37648 //        this.el.setHeight(maxHeight);
37649 //        
37650 //    },
37651     
37652     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37653     {
37654         var pos = this.el.getBox(true);
37655         
37656         var minX = pos.x;
37657         var minY = pos.y;
37658         
37659         var maxX = pos.right;
37660         
37661         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37662         
37663         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37664         
37665         Roo.each(queue, function(box, k){
37666             
37667             Roo.each(box, function(b, kk){
37668                 
37669                 b.el.position('absolute');
37670                 
37671                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37672                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37673                 
37674                 if(b.size == 'md-left' || b.size == 'md-right'){
37675                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37676                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37677                 }
37678                 
37679                 b.el.setWidth(width);
37680                 b.el.setHeight(height);
37681                 
37682             }, this);
37683             
37684             if(!box.length){
37685                 return;
37686             }
37687             
37688             var positions = [];
37689             
37690             switch (box.length){
37691                 case 1 :
37692                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37693                     break;
37694                 case 2 :
37695                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37696                     break;
37697                 case 3 :
37698                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37699                     break;
37700                 case 4 :
37701                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37702                     break;
37703                 default :
37704                     break;
37705             }
37706             
37707             Roo.each(box, function(b,kk){
37708                 
37709                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37710                 
37711                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37712                 
37713             }, this);
37714             
37715         }, this);
37716         
37717     },
37718     
37719     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37720     {
37721         Roo.each(eItems, function(b,k){
37722             
37723             b.size = (k == 0) ? 'sm' : 'xs';
37724             b.x = (k == 0) ? 2 : 1;
37725             b.y = (k == 0) ? 2 : 1;
37726             
37727             b.el.position('absolute');
37728             
37729             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37730                 
37731             b.el.setWidth(width);
37732             
37733             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37734             
37735             b.el.setHeight(height);
37736             
37737         }, this);
37738
37739         var positions = [];
37740         
37741         positions.push({
37742             x : maxX - this.unitWidth * 2 - this.gutter,
37743             y : minY
37744         });
37745         
37746         positions.push({
37747             x : maxX - this.unitWidth,
37748             y : minY + (this.unitWidth + this.gutter) * 2
37749         });
37750         
37751         positions.push({
37752             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37753             y : minY
37754         });
37755         
37756         Roo.each(eItems, function(b,k){
37757             
37758             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37759
37760         }, this);
37761         
37762     },
37763     
37764     getVerticalOneBoxColPositions : function(x, y, box)
37765     {
37766         var pos = [];
37767         
37768         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37769         
37770         if(box[0].size == 'md-left'){
37771             rand = 0;
37772         }
37773         
37774         if(box[0].size == 'md-right'){
37775             rand = 1;
37776         }
37777         
37778         pos.push({
37779             x : x + (this.unitWidth + this.gutter) * rand,
37780             y : y
37781         });
37782         
37783         return pos;
37784     },
37785     
37786     getVerticalTwoBoxColPositions : function(x, y, box)
37787     {
37788         var pos = [];
37789         
37790         if(box[0].size == 'xs'){
37791             
37792             pos.push({
37793                 x : x,
37794                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37795             });
37796
37797             pos.push({
37798                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37799                 y : y
37800             });
37801             
37802             return pos;
37803             
37804         }
37805         
37806         pos.push({
37807             x : x,
37808             y : y
37809         });
37810
37811         pos.push({
37812             x : x + (this.unitWidth + this.gutter) * 2,
37813             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37814         });
37815         
37816         return pos;
37817         
37818     },
37819     
37820     getVerticalThreeBoxColPositions : function(x, y, box)
37821     {
37822         var pos = [];
37823         
37824         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37825             
37826             pos.push({
37827                 x : x,
37828                 y : y
37829             });
37830
37831             pos.push({
37832                 x : x + (this.unitWidth + this.gutter) * 1,
37833                 y : y
37834             });
37835             
37836             pos.push({
37837                 x : x + (this.unitWidth + this.gutter) * 2,
37838                 y : y
37839             });
37840             
37841             return pos;
37842             
37843         }
37844         
37845         if(box[0].size == 'xs' && box[1].size == 'xs'){
37846             
37847             pos.push({
37848                 x : x,
37849                 y : y
37850             });
37851
37852             pos.push({
37853                 x : x,
37854                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37855             });
37856             
37857             pos.push({
37858                 x : x + (this.unitWidth + this.gutter) * 1,
37859                 y : y
37860             });
37861             
37862             return pos;
37863             
37864         }
37865         
37866         pos.push({
37867             x : x,
37868             y : y
37869         });
37870
37871         pos.push({
37872             x : x + (this.unitWidth + this.gutter) * 2,
37873             y : y
37874         });
37875
37876         pos.push({
37877             x : x + (this.unitWidth + this.gutter) * 2,
37878             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37879         });
37880             
37881         return pos;
37882         
37883     },
37884     
37885     getVerticalFourBoxColPositions : function(x, y, box)
37886     {
37887         var pos = [];
37888         
37889         if(box[0].size == 'xs'){
37890             
37891             pos.push({
37892                 x : x,
37893                 y : y
37894             });
37895
37896             pos.push({
37897                 x : x,
37898                 y : y + (this.unitHeight + this.gutter) * 1
37899             });
37900             
37901             pos.push({
37902                 x : x,
37903                 y : y + (this.unitHeight + this.gutter) * 2
37904             });
37905             
37906             pos.push({
37907                 x : x + (this.unitWidth + this.gutter) * 1,
37908                 y : y
37909             });
37910             
37911             return pos;
37912             
37913         }
37914         
37915         pos.push({
37916             x : x,
37917             y : y
37918         });
37919
37920         pos.push({
37921             x : x + (this.unitWidth + this.gutter) * 2,
37922             y : y
37923         });
37924
37925         pos.push({
37926             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37927             y : y + (this.unitHeight + this.gutter) * 1
37928         });
37929
37930         pos.push({
37931             x : x + (this.unitWidth + this.gutter) * 2,
37932             y : y + (this.unitWidth + this.gutter) * 2
37933         });
37934
37935         return pos;
37936         
37937     },
37938     
37939     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37940     {
37941         var pos = [];
37942         
37943         if(box[0].size == 'md-left'){
37944             pos.push({
37945                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37946                 y : minY
37947             });
37948             
37949             return pos;
37950         }
37951         
37952         if(box[0].size == 'md-right'){
37953             pos.push({
37954                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37955                 y : minY + (this.unitWidth + this.gutter) * 1
37956             });
37957             
37958             return pos;
37959         }
37960         
37961         var rand = Math.floor(Math.random() * (4 - box[0].y));
37962         
37963         pos.push({
37964             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37965             y : minY + (this.unitWidth + this.gutter) * rand
37966         });
37967         
37968         return pos;
37969         
37970     },
37971     
37972     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37973     {
37974         var pos = [];
37975         
37976         if(box[0].size == 'xs'){
37977             
37978             pos.push({
37979                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37980                 y : minY
37981             });
37982
37983             pos.push({
37984                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37985                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37986             });
37987             
37988             return pos;
37989             
37990         }
37991         
37992         pos.push({
37993             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37994             y : minY
37995         });
37996
37997         pos.push({
37998             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37999             y : minY + (this.unitWidth + this.gutter) * 2
38000         });
38001         
38002         return pos;
38003         
38004     },
38005     
38006     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38007     {
38008         var pos = [];
38009         
38010         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38011             
38012             pos.push({
38013                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38014                 y : minY
38015             });
38016
38017             pos.push({
38018                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38019                 y : minY + (this.unitWidth + this.gutter) * 1
38020             });
38021             
38022             pos.push({
38023                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38024                 y : minY + (this.unitWidth + this.gutter) * 2
38025             });
38026             
38027             return pos;
38028             
38029         }
38030         
38031         if(box[0].size == 'xs' && box[1].size == 'xs'){
38032             
38033             pos.push({
38034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38035                 y : minY
38036             });
38037
38038             pos.push({
38039                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38040                 y : minY
38041             });
38042             
38043             pos.push({
38044                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38045                 y : minY + (this.unitWidth + this.gutter) * 1
38046             });
38047             
38048             return pos;
38049             
38050         }
38051         
38052         pos.push({
38053             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38054             y : minY
38055         });
38056
38057         pos.push({
38058             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38059             y : minY + (this.unitWidth + this.gutter) * 2
38060         });
38061
38062         pos.push({
38063             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38064             y : minY + (this.unitWidth + this.gutter) * 2
38065         });
38066             
38067         return pos;
38068         
38069     },
38070     
38071     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38072     {
38073         var pos = [];
38074         
38075         if(box[0].size == 'xs'){
38076             
38077             pos.push({
38078                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38079                 y : minY
38080             });
38081
38082             pos.push({
38083                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38084                 y : minY
38085             });
38086             
38087             pos.push({
38088                 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),
38089                 y : minY
38090             });
38091             
38092             pos.push({
38093                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38094                 y : minY + (this.unitWidth + this.gutter) * 1
38095             });
38096             
38097             return pos;
38098             
38099         }
38100         
38101         pos.push({
38102             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38103             y : minY
38104         });
38105         
38106         pos.push({
38107             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38108             y : minY + (this.unitWidth + this.gutter) * 2
38109         });
38110         
38111         pos.push({
38112             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38113             y : minY + (this.unitWidth + this.gutter) * 2
38114         });
38115         
38116         pos.push({
38117             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),
38118             y : minY + (this.unitWidth + this.gutter) * 2
38119         });
38120
38121         return pos;
38122         
38123     },
38124     
38125     /**
38126     * remove a Masonry Brick
38127     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38128     */
38129     removeBrick : function(brick_id)
38130     {
38131         if (!brick_id) {
38132             return;
38133         }
38134         
38135         for (var i = 0; i<this.bricks.length; i++) {
38136             if (this.bricks[i].id == brick_id) {
38137                 this.bricks.splice(i,1);
38138                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38139                 this.initial();
38140             }
38141         }
38142     },
38143     
38144     /**
38145     * adds a Masonry Brick
38146     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38147     */
38148     addBrick : function(cfg)
38149     {
38150         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38151         //this.register(cn);
38152         cn.parentId = this.id;
38153         cn.render(this.el);
38154         return cn;
38155     },
38156     
38157     /**
38158     * register a Masonry Brick
38159     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38160     */
38161     
38162     register : function(brick)
38163     {
38164         this.bricks.push(brick);
38165         brick.masonryId = this.id;
38166     },
38167     
38168     /**
38169     * clear all the Masonry Brick
38170     */
38171     clearAll : function()
38172     {
38173         this.bricks = [];
38174         //this.getChildContainer().dom.innerHTML = "";
38175         this.el.dom.innerHTML = '';
38176     },
38177     
38178     getSelected : function()
38179     {
38180         if (!this.selectedBrick) {
38181             return false;
38182         }
38183         
38184         return this.selectedBrick;
38185     }
38186 });
38187
38188 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38189     
38190     groups: {},
38191      /**
38192     * register a Masonry Layout
38193     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38194     */
38195     
38196     register : function(layout)
38197     {
38198         this.groups[layout.id] = layout;
38199     },
38200     /**
38201     * fetch a  Masonry Layout based on the masonry layout ID
38202     * @param {string} the masonry layout to add
38203     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38204     */
38205     
38206     get: function(layout_id) {
38207         if (typeof(this.groups[layout_id]) == 'undefined') {
38208             return false;
38209         }
38210         return this.groups[layout_id] ;
38211     }
38212     
38213     
38214     
38215 });
38216
38217  
38218
38219  /**
38220  *
38221  * This is based on 
38222  * http://masonry.desandro.com
38223  *
38224  * The idea is to render all the bricks based on vertical width...
38225  *
38226  * The original code extends 'outlayer' - we might need to use that....
38227  * 
38228  */
38229
38230
38231 /**
38232  * @class Roo.bootstrap.LayoutMasonryAuto
38233  * @extends Roo.bootstrap.Component
38234  * Bootstrap Layout Masonry class
38235  * 
38236  * @constructor
38237  * Create a new Element
38238  * @param {Object} config The config object
38239  */
38240
38241 Roo.bootstrap.LayoutMasonryAuto = function(config){
38242     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38243 };
38244
38245 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38246     
38247       /**
38248      * @cfg {Boolean} isFitWidth  - resize the width..
38249      */   
38250     isFitWidth : false,  // options..
38251     /**
38252      * @cfg {Boolean} isOriginLeft = left align?
38253      */   
38254     isOriginLeft : true,
38255     /**
38256      * @cfg {Boolean} isOriginTop = top align?
38257      */   
38258     isOriginTop : false,
38259     /**
38260      * @cfg {Boolean} isLayoutInstant = no animation?
38261      */   
38262     isLayoutInstant : false, // needed?
38263     /**
38264      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38265      */   
38266     isResizingContainer : true,
38267     /**
38268      * @cfg {Number} columnWidth  width of the columns 
38269      */   
38270     
38271     columnWidth : 0,
38272     
38273     /**
38274      * @cfg {Number} maxCols maximum number of columns
38275      */   
38276     
38277     maxCols: 0,
38278     /**
38279      * @cfg {Number} padHeight padding below box..
38280      */   
38281     
38282     padHeight : 10, 
38283     
38284     /**
38285      * @cfg {Boolean} isAutoInitial defalut true
38286      */   
38287     
38288     isAutoInitial : true, 
38289     
38290     // private?
38291     gutter : 0,
38292     
38293     containerWidth: 0,
38294     initialColumnWidth : 0,
38295     currentSize : null,
38296     
38297     colYs : null, // array.
38298     maxY : 0,
38299     padWidth: 10,
38300     
38301     
38302     tag: 'div',
38303     cls: '',
38304     bricks: null, //CompositeElement
38305     cols : 0, // array?
38306     // element : null, // wrapped now this.el
38307     _isLayoutInited : null, 
38308     
38309     
38310     getAutoCreate : function(){
38311         
38312         var cfg = {
38313             tag: this.tag,
38314             cls: 'blog-masonary-wrapper ' + this.cls,
38315             cn : {
38316                 cls : 'mas-boxes masonary'
38317             }
38318         };
38319         
38320         return cfg;
38321     },
38322     
38323     getChildContainer: function( )
38324     {
38325         if (this.boxesEl) {
38326             return this.boxesEl;
38327         }
38328         
38329         this.boxesEl = this.el.select('.mas-boxes').first();
38330         
38331         return this.boxesEl;
38332     },
38333     
38334     
38335     initEvents : function()
38336     {
38337         var _this = this;
38338         
38339         if(this.isAutoInitial){
38340             Roo.log('hook children rendered');
38341             this.on('childrenrendered', function() {
38342                 Roo.log('children rendered');
38343                 _this.initial();
38344             } ,this);
38345         }
38346         
38347     },
38348     
38349     initial : function()
38350     {
38351         this.reloadItems();
38352
38353         this.currentSize = this.el.getBox(true);
38354
38355         /// was window resize... - let's see if this works..
38356         Roo.EventManager.onWindowResize(this.resize, this); 
38357
38358         if(!this.isAutoInitial){
38359             this.layout();
38360             return;
38361         }
38362         
38363         this.layout.defer(500,this);
38364     },
38365     
38366     reloadItems: function()
38367     {
38368         this.bricks = this.el.select('.masonry-brick', true);
38369         
38370         this.bricks.each(function(b) {
38371             //Roo.log(b.getSize());
38372             if (!b.attr('originalwidth')) {
38373                 b.attr('originalwidth',  b.getSize().width);
38374             }
38375             
38376         });
38377         
38378         Roo.log(this.bricks.elements.length);
38379     },
38380     
38381     resize : function()
38382     {
38383         Roo.log('resize');
38384         var cs = this.el.getBox(true);
38385         
38386         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38387             Roo.log("no change in with or X");
38388             return;
38389         }
38390         this.currentSize = cs;
38391         this.layout();
38392     },
38393     
38394     layout : function()
38395     {
38396          Roo.log('layout');
38397         this._resetLayout();
38398         //this._manageStamps();
38399       
38400         // don't animate first layout
38401         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38402         this.layoutItems( isInstant );
38403       
38404         // flag for initalized
38405         this._isLayoutInited = true;
38406     },
38407     
38408     layoutItems : function( isInstant )
38409     {
38410         //var items = this._getItemsForLayout( this.items );
38411         // original code supports filtering layout items.. we just ignore it..
38412         
38413         this._layoutItems( this.bricks , isInstant );
38414       
38415         this._postLayout();
38416     },
38417     _layoutItems : function ( items , isInstant)
38418     {
38419        //this.fireEvent( 'layout', this, items );
38420     
38421
38422         if ( !items || !items.elements.length ) {
38423           // no items, emit event with empty array
38424             return;
38425         }
38426
38427         var queue = [];
38428         items.each(function(item) {
38429             Roo.log("layout item");
38430             Roo.log(item);
38431             // get x/y object from method
38432             var position = this._getItemLayoutPosition( item );
38433             // enqueue
38434             position.item = item;
38435             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38436             queue.push( position );
38437         }, this);
38438       
38439         this._processLayoutQueue( queue );
38440     },
38441     /** Sets position of item in DOM
38442     * @param {Element} item
38443     * @param {Number} x - horizontal position
38444     * @param {Number} y - vertical position
38445     * @param {Boolean} isInstant - disables transitions
38446     */
38447     _processLayoutQueue : function( queue )
38448     {
38449         for ( var i=0, len = queue.length; i < len; i++ ) {
38450             var obj = queue[i];
38451             obj.item.position('absolute');
38452             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38453         }
38454     },
38455       
38456     
38457     /**
38458     * Any logic you want to do after each layout,
38459     * i.e. size the container
38460     */
38461     _postLayout : function()
38462     {
38463         this.resizeContainer();
38464     },
38465     
38466     resizeContainer : function()
38467     {
38468         if ( !this.isResizingContainer ) {
38469             return;
38470         }
38471         var size = this._getContainerSize();
38472         if ( size ) {
38473             this.el.setSize(size.width,size.height);
38474             this.boxesEl.setSize(size.width,size.height);
38475         }
38476     },
38477     
38478     
38479     
38480     _resetLayout : function()
38481     {
38482         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38483         this.colWidth = this.el.getWidth();
38484         //this.gutter = this.el.getWidth(); 
38485         
38486         this.measureColumns();
38487
38488         // reset column Y
38489         var i = this.cols;
38490         this.colYs = [];
38491         while (i--) {
38492             this.colYs.push( 0 );
38493         }
38494     
38495         this.maxY = 0;
38496     },
38497
38498     measureColumns : function()
38499     {
38500         this.getContainerWidth();
38501       // if columnWidth is 0, default to outerWidth of first item
38502         if ( !this.columnWidth ) {
38503             var firstItem = this.bricks.first();
38504             Roo.log(firstItem);
38505             this.columnWidth  = this.containerWidth;
38506             if (firstItem && firstItem.attr('originalwidth') ) {
38507                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38508             }
38509             // columnWidth fall back to item of first element
38510             Roo.log("set column width?");
38511                         this.initialColumnWidth = this.columnWidth  ;
38512
38513             // if first elem has no width, default to size of container
38514             
38515         }
38516         
38517         
38518         if (this.initialColumnWidth) {
38519             this.columnWidth = this.initialColumnWidth;
38520         }
38521         
38522         
38523             
38524         // column width is fixed at the top - however if container width get's smaller we should
38525         // reduce it...
38526         
38527         // this bit calcs how man columns..
38528             
38529         var columnWidth = this.columnWidth += this.gutter;
38530       
38531         // calculate columns
38532         var containerWidth = this.containerWidth + this.gutter;
38533         
38534         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38535         // fix rounding errors, typically with gutters
38536         var excess = columnWidth - containerWidth % columnWidth;
38537         
38538         
38539         // if overshoot is less than a pixel, round up, otherwise floor it
38540         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38541         cols = Math[ mathMethod ]( cols );
38542         this.cols = Math.max( cols, 1 );
38543         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38544         
38545          // padding positioning..
38546         var totalColWidth = this.cols * this.columnWidth;
38547         var padavail = this.containerWidth - totalColWidth;
38548         // so for 2 columns - we need 3 'pads'
38549         
38550         var padNeeded = (1+this.cols) * this.padWidth;
38551         
38552         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38553         
38554         this.columnWidth += padExtra
38555         //this.padWidth = Math.floor(padavail /  ( this.cols));
38556         
38557         // adjust colum width so that padding is fixed??
38558         
38559         // we have 3 columns ... total = width * 3
38560         // we have X left over... that should be used by 
38561         
38562         //if (this.expandC) {
38563             
38564         //}
38565         
38566         
38567         
38568     },
38569     
38570     getContainerWidth : function()
38571     {
38572        /* // container is parent if fit width
38573         var container = this.isFitWidth ? this.element.parentNode : this.element;
38574         // check that this.size and size are there
38575         // IE8 triggers resize on body size change, so they might not be
38576         
38577         var size = getSize( container );  //FIXME
38578         this.containerWidth = size && size.innerWidth; //FIXME
38579         */
38580          
38581         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38582         
38583     },
38584     
38585     _getItemLayoutPosition : function( item )  // what is item?
38586     {
38587         // we resize the item to our columnWidth..
38588       
38589         item.setWidth(this.columnWidth);
38590         item.autoBoxAdjust  = false;
38591         
38592         var sz = item.getSize();
38593  
38594         // how many columns does this brick span
38595         var remainder = this.containerWidth % this.columnWidth;
38596         
38597         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38598         // round if off by 1 pixel, otherwise use ceil
38599         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38600         colSpan = Math.min( colSpan, this.cols );
38601         
38602         // normally this should be '1' as we dont' currently allow multi width columns..
38603         
38604         var colGroup = this._getColGroup( colSpan );
38605         // get the minimum Y value from the columns
38606         var minimumY = Math.min.apply( Math, colGroup );
38607         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38608         
38609         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38610          
38611         // position the brick
38612         var position = {
38613             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38614             y: this.currentSize.y + minimumY + this.padHeight
38615         };
38616         
38617         Roo.log(position);
38618         // apply setHeight to necessary columns
38619         var setHeight = minimumY + sz.height + this.padHeight;
38620         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38621         
38622         var setSpan = this.cols + 1 - colGroup.length;
38623         for ( var i = 0; i < setSpan; i++ ) {
38624           this.colYs[ shortColIndex + i ] = setHeight ;
38625         }
38626       
38627         return position;
38628     },
38629     
38630     /**
38631      * @param {Number} colSpan - number of columns the element spans
38632      * @returns {Array} colGroup
38633      */
38634     _getColGroup : function( colSpan )
38635     {
38636         if ( colSpan < 2 ) {
38637           // if brick spans only one column, use all the column Ys
38638           return this.colYs;
38639         }
38640       
38641         var colGroup = [];
38642         // how many different places could this brick fit horizontally
38643         var groupCount = this.cols + 1 - colSpan;
38644         // for each group potential horizontal position
38645         for ( var i = 0; i < groupCount; i++ ) {
38646           // make an array of colY values for that one group
38647           var groupColYs = this.colYs.slice( i, i + colSpan );
38648           // and get the max value of the array
38649           colGroup[i] = Math.max.apply( Math, groupColYs );
38650         }
38651         return colGroup;
38652     },
38653     /*
38654     _manageStamp : function( stamp )
38655     {
38656         var stampSize =  stamp.getSize();
38657         var offset = stamp.getBox();
38658         // get the columns that this stamp affects
38659         var firstX = this.isOriginLeft ? offset.x : offset.right;
38660         var lastX = firstX + stampSize.width;
38661         var firstCol = Math.floor( firstX / this.columnWidth );
38662         firstCol = Math.max( 0, firstCol );
38663         
38664         var lastCol = Math.floor( lastX / this.columnWidth );
38665         // lastCol should not go over if multiple of columnWidth #425
38666         lastCol -= lastX % this.columnWidth ? 0 : 1;
38667         lastCol = Math.min( this.cols - 1, lastCol );
38668         
38669         // set colYs to bottom of the stamp
38670         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38671             stampSize.height;
38672             
38673         for ( var i = firstCol; i <= lastCol; i++ ) {
38674           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38675         }
38676     },
38677     */
38678     
38679     _getContainerSize : function()
38680     {
38681         this.maxY = Math.max.apply( Math, this.colYs );
38682         var size = {
38683             height: this.maxY
38684         };
38685       
38686         if ( this.isFitWidth ) {
38687             size.width = this._getContainerFitWidth();
38688         }
38689       
38690         return size;
38691     },
38692     
38693     _getContainerFitWidth : function()
38694     {
38695         var unusedCols = 0;
38696         // count unused columns
38697         var i = this.cols;
38698         while ( --i ) {
38699           if ( this.colYs[i] !== 0 ) {
38700             break;
38701           }
38702           unusedCols++;
38703         }
38704         // fit container to columns that have been used
38705         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38706     },
38707     
38708     needsResizeLayout : function()
38709     {
38710         var previousWidth = this.containerWidth;
38711         this.getContainerWidth();
38712         return previousWidth !== this.containerWidth;
38713     }
38714  
38715 });
38716
38717  
38718
38719  /*
38720  * - LGPL
38721  *
38722  * element
38723  * 
38724  */
38725
38726 /**
38727  * @class Roo.bootstrap.MasonryBrick
38728  * @extends Roo.bootstrap.Component
38729  * Bootstrap MasonryBrick class
38730  * 
38731  * @constructor
38732  * Create a new MasonryBrick
38733  * @param {Object} config The config object
38734  */
38735
38736 Roo.bootstrap.MasonryBrick = function(config){
38737     
38738     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38739     
38740     Roo.bootstrap.MasonryBrick.register(this);
38741     
38742     this.addEvents({
38743         // raw events
38744         /**
38745          * @event click
38746          * When a MasonryBrick is clcik
38747          * @param {Roo.bootstrap.MasonryBrick} this
38748          * @param {Roo.EventObject} e
38749          */
38750         "click" : true
38751     });
38752 };
38753
38754 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38755     
38756     /**
38757      * @cfg {String} title
38758      */   
38759     title : '',
38760     /**
38761      * @cfg {String} html
38762      */   
38763     html : '',
38764     /**
38765      * @cfg {String} bgimage
38766      */   
38767     bgimage : '',
38768     /**
38769      * @cfg {String} videourl
38770      */   
38771     videourl : '',
38772     /**
38773      * @cfg {String} cls
38774      */   
38775     cls : '',
38776     /**
38777      * @cfg {String} href
38778      */   
38779     href : '',
38780     /**
38781      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38782      */   
38783     size : 'xs',
38784     
38785     /**
38786      * @cfg {String} placetitle (center|bottom)
38787      */   
38788     placetitle : '',
38789     
38790     /**
38791      * @cfg {Boolean} isFitContainer defalut true
38792      */   
38793     isFitContainer : true, 
38794     
38795     /**
38796      * @cfg {Boolean} preventDefault defalut false
38797      */   
38798     preventDefault : false, 
38799     
38800     /**
38801      * @cfg {Boolean} inverse defalut false
38802      */   
38803     maskInverse : false, 
38804     
38805     getAutoCreate : function()
38806     {
38807         if(!this.isFitContainer){
38808             return this.getSplitAutoCreate();
38809         }
38810         
38811         var cls = 'masonry-brick masonry-brick-full';
38812         
38813         if(this.href.length){
38814             cls += ' masonry-brick-link';
38815         }
38816         
38817         if(this.bgimage.length){
38818             cls += ' masonry-brick-image';
38819         }
38820         
38821         if(this.maskInverse){
38822             cls += ' mask-inverse';
38823         }
38824         
38825         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38826             cls += ' enable-mask';
38827         }
38828         
38829         if(this.size){
38830             cls += ' masonry-' + this.size + '-brick';
38831         }
38832         
38833         if(this.placetitle.length){
38834             
38835             switch (this.placetitle) {
38836                 case 'center' :
38837                     cls += ' masonry-center-title';
38838                     break;
38839                 case 'bottom' :
38840                     cls += ' masonry-bottom-title';
38841                     break;
38842                 default:
38843                     break;
38844             }
38845             
38846         } else {
38847             if(!this.html.length && !this.bgimage.length){
38848                 cls += ' masonry-center-title';
38849             }
38850
38851             if(!this.html.length && this.bgimage.length){
38852                 cls += ' masonry-bottom-title';
38853             }
38854         }
38855         
38856         if(this.cls){
38857             cls += ' ' + this.cls;
38858         }
38859         
38860         var cfg = {
38861             tag: (this.href.length) ? 'a' : 'div',
38862             cls: cls,
38863             cn: [
38864                 {
38865                     tag: 'div',
38866                     cls: 'masonry-brick-mask'
38867                 },
38868                 {
38869                     tag: 'div',
38870                     cls: 'masonry-brick-paragraph',
38871                     cn: []
38872                 }
38873             ]
38874         };
38875         
38876         if(this.href.length){
38877             cfg.href = this.href;
38878         }
38879         
38880         var cn = cfg.cn[1].cn;
38881         
38882         if(this.title.length){
38883             cn.push({
38884                 tag: 'h4',
38885                 cls: 'masonry-brick-title',
38886                 html: this.title
38887             });
38888         }
38889         
38890         if(this.html.length){
38891             cn.push({
38892                 tag: 'p',
38893                 cls: 'masonry-brick-text',
38894                 html: this.html
38895             });
38896         }
38897         
38898         if (!this.title.length && !this.html.length) {
38899             cfg.cn[1].cls += ' hide';
38900         }
38901         
38902         if(this.bgimage.length){
38903             cfg.cn.push({
38904                 tag: 'img',
38905                 cls: 'masonry-brick-image-view',
38906                 src: this.bgimage
38907             });
38908         }
38909         
38910         if(this.videourl.length){
38911             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38912             // youtube support only?
38913             cfg.cn.push({
38914                 tag: 'iframe',
38915                 cls: 'masonry-brick-image-view',
38916                 src: vurl,
38917                 frameborder : 0,
38918                 allowfullscreen : true
38919             });
38920         }
38921         
38922         return cfg;
38923         
38924     },
38925     
38926     getSplitAutoCreate : function()
38927     {
38928         var cls = 'masonry-brick masonry-brick-split';
38929         
38930         if(this.href.length){
38931             cls += ' masonry-brick-link';
38932         }
38933         
38934         if(this.bgimage.length){
38935             cls += ' masonry-brick-image';
38936         }
38937         
38938         if(this.size){
38939             cls += ' masonry-' + this.size + '-brick';
38940         }
38941         
38942         switch (this.placetitle) {
38943             case 'center' :
38944                 cls += ' masonry-center-title';
38945                 break;
38946             case 'bottom' :
38947                 cls += ' masonry-bottom-title';
38948                 break;
38949             default:
38950                 if(!this.bgimage.length){
38951                     cls += ' masonry-center-title';
38952                 }
38953
38954                 if(this.bgimage.length){
38955                     cls += ' masonry-bottom-title';
38956                 }
38957                 break;
38958         }
38959         
38960         if(this.cls){
38961             cls += ' ' + this.cls;
38962         }
38963         
38964         var cfg = {
38965             tag: (this.href.length) ? 'a' : 'div',
38966             cls: cls,
38967             cn: [
38968                 {
38969                     tag: 'div',
38970                     cls: 'masonry-brick-split-head',
38971                     cn: [
38972                         {
38973                             tag: 'div',
38974                             cls: 'masonry-brick-paragraph',
38975                             cn: []
38976                         }
38977                     ]
38978                 },
38979                 {
38980                     tag: 'div',
38981                     cls: 'masonry-brick-split-body',
38982                     cn: []
38983                 }
38984             ]
38985         };
38986         
38987         if(this.href.length){
38988             cfg.href = this.href;
38989         }
38990         
38991         if(this.title.length){
38992             cfg.cn[0].cn[0].cn.push({
38993                 tag: 'h4',
38994                 cls: 'masonry-brick-title',
38995                 html: this.title
38996             });
38997         }
38998         
38999         if(this.html.length){
39000             cfg.cn[1].cn.push({
39001                 tag: 'p',
39002                 cls: 'masonry-brick-text',
39003                 html: this.html
39004             });
39005         }
39006
39007         if(this.bgimage.length){
39008             cfg.cn[0].cn.push({
39009                 tag: 'img',
39010                 cls: 'masonry-brick-image-view',
39011                 src: this.bgimage
39012             });
39013         }
39014         
39015         if(this.videourl.length){
39016             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39017             // youtube support only?
39018             cfg.cn[0].cn.cn.push({
39019                 tag: 'iframe',
39020                 cls: 'masonry-brick-image-view',
39021                 src: vurl,
39022                 frameborder : 0,
39023                 allowfullscreen : true
39024             });
39025         }
39026         
39027         return cfg;
39028     },
39029     
39030     initEvents: function() 
39031     {
39032         switch (this.size) {
39033             case 'xs' :
39034                 this.x = 1;
39035                 this.y = 1;
39036                 break;
39037             case 'sm' :
39038                 this.x = 2;
39039                 this.y = 2;
39040                 break;
39041             case 'md' :
39042             case 'md-left' :
39043             case 'md-right' :
39044                 this.x = 3;
39045                 this.y = 3;
39046                 break;
39047             case 'tall' :
39048                 this.x = 2;
39049                 this.y = 3;
39050                 break;
39051             case 'wide' :
39052                 this.x = 3;
39053                 this.y = 2;
39054                 break;
39055             case 'wide-thin' :
39056                 this.x = 3;
39057                 this.y = 1;
39058                 break;
39059                         
39060             default :
39061                 break;
39062         }
39063         
39064         if(Roo.isTouch){
39065             this.el.on('touchstart', this.onTouchStart, this);
39066             this.el.on('touchmove', this.onTouchMove, this);
39067             this.el.on('touchend', this.onTouchEnd, this);
39068             this.el.on('contextmenu', this.onContextMenu, this);
39069         } else {
39070             this.el.on('mouseenter'  ,this.enter, this);
39071             this.el.on('mouseleave', this.leave, this);
39072             this.el.on('click', this.onClick, this);
39073         }
39074         
39075         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39076             this.parent().bricks.push(this);   
39077         }
39078         
39079     },
39080     
39081     onClick: function(e, el)
39082     {
39083         var time = this.endTimer - this.startTimer;
39084         // Roo.log(e.preventDefault());
39085         if(Roo.isTouch){
39086             if(time > 1000){
39087                 e.preventDefault();
39088                 return;
39089             }
39090         }
39091         
39092         if(!this.preventDefault){
39093             return;
39094         }
39095         
39096         e.preventDefault();
39097         
39098         if (this.activeClass != '') {
39099             this.selectBrick();
39100         }
39101         
39102         this.fireEvent('click', this, e);
39103     },
39104     
39105     enter: function(e, el)
39106     {
39107         e.preventDefault();
39108         
39109         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39110             return;
39111         }
39112         
39113         if(this.bgimage.length && this.html.length){
39114             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39115         }
39116     },
39117     
39118     leave: function(e, el)
39119     {
39120         e.preventDefault();
39121         
39122         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39123             return;
39124         }
39125         
39126         if(this.bgimage.length && this.html.length){
39127             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39128         }
39129     },
39130     
39131     onTouchStart: function(e, el)
39132     {
39133 //        e.preventDefault();
39134         
39135         this.touchmoved = false;
39136         
39137         if(!this.isFitContainer){
39138             return;
39139         }
39140         
39141         if(!this.bgimage.length || !this.html.length){
39142             return;
39143         }
39144         
39145         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39146         
39147         this.timer = new Date().getTime();
39148         
39149     },
39150     
39151     onTouchMove: function(e, el)
39152     {
39153         this.touchmoved = true;
39154     },
39155     
39156     onContextMenu : function(e,el)
39157     {
39158         e.preventDefault();
39159         e.stopPropagation();
39160         return false;
39161     },
39162     
39163     onTouchEnd: function(e, el)
39164     {
39165 //        e.preventDefault();
39166         
39167         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39168         
39169             this.leave(e,el);
39170             
39171             return;
39172         }
39173         
39174         if(!this.bgimage.length || !this.html.length){
39175             
39176             if(this.href.length){
39177                 window.location.href = this.href;
39178             }
39179             
39180             return;
39181         }
39182         
39183         if(!this.isFitContainer){
39184             return;
39185         }
39186         
39187         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39188         
39189         window.location.href = this.href;
39190     },
39191     
39192     //selection on single brick only
39193     selectBrick : function() {
39194         
39195         if (!this.parentId) {
39196             return;
39197         }
39198         
39199         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39200         var index = m.selectedBrick.indexOf(this.id);
39201         
39202         if ( index > -1) {
39203             m.selectedBrick.splice(index,1);
39204             this.el.removeClass(this.activeClass);
39205             return;
39206         }
39207         
39208         for(var i = 0; i < m.selectedBrick.length; i++) {
39209             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39210             b.el.removeClass(b.activeClass);
39211         }
39212         
39213         m.selectedBrick = [];
39214         
39215         m.selectedBrick.push(this.id);
39216         this.el.addClass(this.activeClass);
39217         return;
39218     },
39219     
39220     isSelected : function(){
39221         return this.el.hasClass(this.activeClass);
39222         
39223     }
39224 });
39225
39226 Roo.apply(Roo.bootstrap.MasonryBrick, {
39227     
39228     //groups: {},
39229     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39230      /**
39231     * register a Masonry Brick
39232     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39233     */
39234     
39235     register : function(brick)
39236     {
39237         //this.groups[brick.id] = brick;
39238         this.groups.add(brick.id, brick);
39239     },
39240     /**
39241     * fetch a  masonry brick based on the masonry brick ID
39242     * @param {string} the masonry brick to add
39243     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39244     */
39245     
39246     get: function(brick_id) 
39247     {
39248         // if (typeof(this.groups[brick_id]) == 'undefined') {
39249         //     return false;
39250         // }
39251         // return this.groups[brick_id] ;
39252         
39253         if(this.groups.key(brick_id)) {
39254             return this.groups.key(brick_id);
39255         }
39256         
39257         return false;
39258     }
39259     
39260     
39261     
39262 });
39263
39264  /*
39265  * - LGPL
39266  *
39267  * element
39268  * 
39269  */
39270
39271 /**
39272  * @class Roo.bootstrap.Brick
39273  * @extends Roo.bootstrap.Component
39274  * Bootstrap Brick class
39275  * 
39276  * @constructor
39277  * Create a new Brick
39278  * @param {Object} config The config object
39279  */
39280
39281 Roo.bootstrap.Brick = function(config){
39282     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39283     
39284     this.addEvents({
39285         // raw events
39286         /**
39287          * @event click
39288          * When a Brick is click
39289          * @param {Roo.bootstrap.Brick} this
39290          * @param {Roo.EventObject} e
39291          */
39292         "click" : true
39293     });
39294 };
39295
39296 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39297     
39298     /**
39299      * @cfg {String} title
39300      */   
39301     title : '',
39302     /**
39303      * @cfg {String} html
39304      */   
39305     html : '',
39306     /**
39307      * @cfg {String} bgimage
39308      */   
39309     bgimage : '',
39310     /**
39311      * @cfg {String} cls
39312      */   
39313     cls : '',
39314     /**
39315      * @cfg {String} href
39316      */   
39317     href : '',
39318     /**
39319      * @cfg {String} video
39320      */   
39321     video : '',
39322     /**
39323      * @cfg {Boolean} square
39324      */   
39325     square : true,
39326     
39327     getAutoCreate : function()
39328     {
39329         var cls = 'roo-brick';
39330         
39331         if(this.href.length){
39332             cls += ' roo-brick-link';
39333         }
39334         
39335         if(this.bgimage.length){
39336             cls += ' roo-brick-image';
39337         }
39338         
39339         if(!this.html.length && !this.bgimage.length){
39340             cls += ' roo-brick-center-title';
39341         }
39342         
39343         if(!this.html.length && this.bgimage.length){
39344             cls += ' roo-brick-bottom-title';
39345         }
39346         
39347         if(this.cls){
39348             cls += ' ' + this.cls;
39349         }
39350         
39351         var cfg = {
39352             tag: (this.href.length) ? 'a' : 'div',
39353             cls: cls,
39354             cn: [
39355                 {
39356                     tag: 'div',
39357                     cls: 'roo-brick-paragraph',
39358                     cn: []
39359                 }
39360             ]
39361         };
39362         
39363         if(this.href.length){
39364             cfg.href = this.href;
39365         }
39366         
39367         var cn = cfg.cn[0].cn;
39368         
39369         if(this.title.length){
39370             cn.push({
39371                 tag: 'h4',
39372                 cls: 'roo-brick-title',
39373                 html: this.title
39374             });
39375         }
39376         
39377         if(this.html.length){
39378             cn.push({
39379                 tag: 'p',
39380                 cls: 'roo-brick-text',
39381                 html: this.html
39382             });
39383         } else {
39384             cn.cls += ' hide';
39385         }
39386         
39387         if(this.bgimage.length){
39388             cfg.cn.push({
39389                 tag: 'img',
39390                 cls: 'roo-brick-image-view',
39391                 src: this.bgimage
39392             });
39393         }
39394         
39395         return cfg;
39396     },
39397     
39398     initEvents: function() 
39399     {
39400         if(this.title.length || this.html.length){
39401             this.el.on('mouseenter'  ,this.enter, this);
39402             this.el.on('mouseleave', this.leave, this);
39403         }
39404         
39405         Roo.EventManager.onWindowResize(this.resize, this); 
39406         
39407         if(this.bgimage.length){
39408             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39409             this.imageEl.on('load', this.onImageLoad, this);
39410             return;
39411         }
39412         
39413         this.resize();
39414     },
39415     
39416     onImageLoad : function()
39417     {
39418         this.resize();
39419     },
39420     
39421     resize : function()
39422     {
39423         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39424         
39425         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39426         
39427         if(this.bgimage.length){
39428             var image = this.el.select('.roo-brick-image-view', true).first();
39429             
39430             image.setWidth(paragraph.getWidth());
39431             
39432             if(this.square){
39433                 image.setHeight(paragraph.getWidth());
39434             }
39435             
39436             this.el.setHeight(image.getHeight());
39437             paragraph.setHeight(image.getHeight());
39438             
39439         }
39440         
39441     },
39442     
39443     enter: function(e, el)
39444     {
39445         e.preventDefault();
39446         
39447         if(this.bgimage.length){
39448             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39449             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39450         }
39451     },
39452     
39453     leave: function(e, el)
39454     {
39455         e.preventDefault();
39456         
39457         if(this.bgimage.length){
39458             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39459             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39460         }
39461     }
39462     
39463 });
39464
39465  
39466
39467  /*
39468  * - LGPL
39469  *
39470  * Number field 
39471  */
39472
39473 /**
39474  * @class Roo.bootstrap.form.NumberField
39475  * @extends Roo.bootstrap.form.Input
39476  * Bootstrap NumberField class
39477  * 
39478  * 
39479  * 
39480  * 
39481  * @constructor
39482  * Create a new NumberField
39483  * @param {Object} config The config object
39484  */
39485
39486 Roo.bootstrap.form.NumberField = function(config){
39487     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39488 };
39489
39490 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39491     
39492     /**
39493      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39494      */
39495     allowDecimals : true,
39496     /**
39497      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39498      */
39499     decimalSeparator : ".",
39500     /**
39501      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39502      */
39503     decimalPrecision : 2,
39504     /**
39505      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39506      */
39507     allowNegative : true,
39508     
39509     /**
39510      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39511      */
39512     allowZero: true,
39513     /**
39514      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39515      */
39516     minValue : Number.NEGATIVE_INFINITY,
39517     /**
39518      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39519      */
39520     maxValue : Number.MAX_VALUE,
39521     /**
39522      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39523      */
39524     minText : "The minimum value for this field is {0}",
39525     /**
39526      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39527      */
39528     maxText : "The maximum value for this field is {0}",
39529     /**
39530      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39531      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39532      */
39533     nanText : "{0} is not a valid number",
39534     /**
39535      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39536      */
39537     thousandsDelimiter : false,
39538     /**
39539      * @cfg {String} valueAlign alignment of value
39540      */
39541     valueAlign : "left",
39542
39543     getAutoCreate : function()
39544     {
39545         var hiddenInput = {
39546             tag: 'input',
39547             type: 'hidden',
39548             id: Roo.id(),
39549             cls: 'hidden-number-input'
39550         };
39551         
39552         if (this.name) {
39553             hiddenInput.name = this.name;
39554         }
39555         
39556         this.name = '';
39557         
39558         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39559         
39560         this.name = hiddenInput.name;
39561         
39562         if(cfg.cn.length > 0) {
39563             cfg.cn.push(hiddenInput);
39564         }
39565         
39566         return cfg;
39567     },
39568
39569     // private
39570     initEvents : function()
39571     {   
39572         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39573         
39574         var allowed = "0123456789";
39575         
39576         if(this.allowDecimals){
39577             allowed += this.decimalSeparator;
39578         }
39579         
39580         if(this.allowNegative){
39581             allowed += "-";
39582         }
39583         
39584         if(this.thousandsDelimiter) {
39585             allowed += ",";
39586         }
39587         
39588         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39589         
39590         var keyPress = function(e){
39591             
39592             var k = e.getKey();
39593             
39594             var c = e.getCharCode();
39595             
39596             if(
39597                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39598                     allowed.indexOf(String.fromCharCode(c)) === -1
39599             ){
39600                 e.stopEvent();
39601                 return;
39602             }
39603             
39604             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39605                 return;
39606             }
39607             
39608             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39609                 e.stopEvent();
39610             }
39611         };
39612         
39613         this.el.on("keypress", keyPress, this);
39614     },
39615     
39616     validateValue : function(value)
39617     {
39618         
39619         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39620             return false;
39621         }
39622         
39623         var num = this.parseValue(value);
39624         
39625         if(isNaN(num)){
39626             this.markInvalid(String.format(this.nanText, value));
39627             return false;
39628         }
39629         
39630         if(num < this.minValue){
39631             this.markInvalid(String.format(this.minText, this.minValue));
39632             return false;
39633         }
39634         
39635         if(num > this.maxValue){
39636             this.markInvalid(String.format(this.maxText, this.maxValue));
39637             return false;
39638         }
39639         
39640         return true;
39641     },
39642
39643     getValue : function()
39644     {
39645         var v = this.hiddenEl().getValue();
39646         
39647         return this.fixPrecision(this.parseValue(v));
39648     },
39649
39650     parseValue : function(value)
39651     {
39652         if(this.thousandsDelimiter) {
39653             value += "";
39654             r = new RegExp(",", "g");
39655             value = value.replace(r, "");
39656         }
39657         
39658         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39659         return isNaN(value) ? '' : value;
39660     },
39661
39662     fixPrecision : function(value)
39663     {
39664         if(this.thousandsDelimiter) {
39665             value += "";
39666             r = new RegExp(",", "g");
39667             value = value.replace(r, "");
39668         }
39669         
39670         var nan = isNaN(value);
39671         
39672         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39673             return nan ? '' : value;
39674         }
39675         return parseFloat(value).toFixed(this.decimalPrecision);
39676     },
39677
39678     setValue : function(v)
39679     {
39680         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39681         
39682         this.value = v;
39683         
39684         if(this.rendered){
39685             
39686             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39687             
39688             this.inputEl().dom.value = (v == '') ? '' :
39689                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39690             
39691             if(!this.allowZero && v === '0') {
39692                 this.hiddenEl().dom.value = '';
39693                 this.inputEl().dom.value = '';
39694             }
39695             
39696             this.validate();
39697         }
39698     },
39699
39700     decimalPrecisionFcn : function(v)
39701     {
39702         return Math.floor(v);
39703     },
39704
39705     beforeBlur : function()
39706     {
39707         var v = this.parseValue(this.getRawValue());
39708         
39709         if(v || v === 0 || v === ''){
39710             this.setValue(v);
39711         }
39712     },
39713     
39714     hiddenEl : function()
39715     {
39716         return this.el.select('input.hidden-number-input',true).first();
39717     }
39718     
39719 });
39720
39721  
39722
39723 /*
39724 * Licence: LGPL
39725 */
39726
39727 /**
39728  * @class Roo.bootstrap.DocumentSlider
39729  * @extends Roo.bootstrap.Component
39730  * Bootstrap DocumentSlider class
39731  * 
39732  * @constructor
39733  * Create a new DocumentViewer
39734  * @param {Object} config The config object
39735  */
39736
39737 Roo.bootstrap.DocumentSlider = function(config){
39738     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39739     
39740     this.files = [];
39741     
39742     this.addEvents({
39743         /**
39744          * @event initial
39745          * Fire after initEvent
39746          * @param {Roo.bootstrap.DocumentSlider} this
39747          */
39748         "initial" : true,
39749         /**
39750          * @event update
39751          * Fire after update
39752          * @param {Roo.bootstrap.DocumentSlider} this
39753          */
39754         "update" : true,
39755         /**
39756          * @event click
39757          * Fire after click
39758          * @param {Roo.bootstrap.DocumentSlider} this
39759          */
39760         "click" : true
39761     });
39762 };
39763
39764 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39765     
39766     files : false,
39767     
39768     indicator : 0,
39769     
39770     getAutoCreate : function()
39771     {
39772         var cfg = {
39773             tag : 'div',
39774             cls : 'roo-document-slider',
39775             cn : [
39776                 {
39777                     tag : 'div',
39778                     cls : 'roo-document-slider-header',
39779                     cn : [
39780                         {
39781                             tag : 'div',
39782                             cls : 'roo-document-slider-header-title'
39783                         }
39784                     ]
39785                 },
39786                 {
39787                     tag : 'div',
39788                     cls : 'roo-document-slider-body',
39789                     cn : [
39790                         {
39791                             tag : 'div',
39792                             cls : 'roo-document-slider-prev',
39793                             cn : [
39794                                 {
39795                                     tag : 'i',
39796                                     cls : 'fa fa-chevron-left'
39797                                 }
39798                             ]
39799                         },
39800                         {
39801                             tag : 'div',
39802                             cls : 'roo-document-slider-thumb',
39803                             cn : [
39804                                 {
39805                                     tag : 'img',
39806                                     cls : 'roo-document-slider-image'
39807                                 }
39808                             ]
39809                         },
39810                         {
39811                             tag : 'div',
39812                             cls : 'roo-document-slider-next',
39813                             cn : [
39814                                 {
39815                                     tag : 'i',
39816                                     cls : 'fa fa-chevron-right'
39817                                 }
39818                             ]
39819                         }
39820                     ]
39821                 }
39822             ]
39823         };
39824         
39825         return cfg;
39826     },
39827     
39828     initEvents : function()
39829     {
39830         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39831         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39832         
39833         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39834         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39835         
39836         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39837         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39838         
39839         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39840         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39841         
39842         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39843         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39844         
39845         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39846         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39847         
39848         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39849         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39850         
39851         this.thumbEl.on('click', this.onClick, this);
39852         
39853         this.prevIndicator.on('click', this.prev, this);
39854         
39855         this.nextIndicator.on('click', this.next, this);
39856         
39857     },
39858     
39859     initial : function()
39860     {
39861         if(this.files.length){
39862             this.indicator = 1;
39863             this.update()
39864         }
39865         
39866         this.fireEvent('initial', this);
39867     },
39868     
39869     update : function()
39870     {
39871         this.imageEl.attr('src', this.files[this.indicator - 1]);
39872         
39873         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39874         
39875         this.prevIndicator.show();
39876         
39877         if(this.indicator == 1){
39878             this.prevIndicator.hide();
39879         }
39880         
39881         this.nextIndicator.show();
39882         
39883         if(this.indicator == this.files.length){
39884             this.nextIndicator.hide();
39885         }
39886         
39887         this.thumbEl.scrollTo('top');
39888         
39889         this.fireEvent('update', this);
39890     },
39891     
39892     onClick : function(e)
39893     {
39894         e.preventDefault();
39895         
39896         this.fireEvent('click', this);
39897     },
39898     
39899     prev : function(e)
39900     {
39901         e.preventDefault();
39902         
39903         this.indicator = Math.max(1, this.indicator - 1);
39904         
39905         this.update();
39906     },
39907     
39908     next : function(e)
39909     {
39910         e.preventDefault();
39911         
39912         this.indicator = Math.min(this.files.length, this.indicator + 1);
39913         
39914         this.update();
39915     }
39916 });
39917 /*
39918  * - LGPL
39919  *
39920  * RadioSet
39921  *
39922  *
39923  */
39924
39925 /**
39926  * @class Roo.bootstrap.form.RadioSet
39927  * @extends Roo.bootstrap.form.Input
39928  * @children Roo.bootstrap.form.Radio
39929  * Bootstrap RadioSet class
39930  * @cfg {String} indicatorpos (left|right) default left
39931  * @cfg {Boolean} inline (true|false) inline the element (default true)
39932  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39933  * @constructor
39934  * Create a new RadioSet
39935  * @param {Object} config The config object
39936  */
39937
39938 Roo.bootstrap.form.RadioSet = function(config){
39939     
39940     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39941     
39942     this.radioes = [];
39943     
39944     Roo.bootstrap.form.RadioSet.register(this);
39945     
39946     this.addEvents({
39947         /**
39948         * @event check
39949         * Fires when the element is checked or unchecked.
39950         * @param {Roo.bootstrap.form.RadioSet} this This radio
39951         * @param {Roo.bootstrap.form.Radio} item The checked item
39952         */
39953        check : true,
39954        /**
39955         * @event click
39956         * Fires when the element is click.
39957         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39958         * @param {Roo.bootstrap.form.Radio} item The checked item
39959         * @param {Roo.EventObject} e The event object
39960         */
39961        click : true
39962     });
39963     
39964 };
39965
39966 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39967
39968     radioes : false,
39969     
39970     inline : true,
39971     
39972     weight : '',
39973     
39974     indicatorpos : 'left',
39975     
39976     getAutoCreate : function()
39977     {
39978         var label = {
39979             tag : 'label',
39980             cls : 'roo-radio-set-label',
39981             cn : [
39982                 {
39983                     tag : 'span',
39984                     html : this.fieldLabel
39985                 }
39986             ]
39987         };
39988         if (Roo.bootstrap.version == 3) {
39989             
39990             
39991             if(this.indicatorpos == 'left'){
39992                 label.cn.unshift({
39993                     tag : 'i',
39994                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39995                     tooltip : 'This field is required'
39996                 });
39997             } else {
39998                 label.cn.push({
39999                     tag : 'i',
40000                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
40001                     tooltip : 'This field is required'
40002                 });
40003             }
40004         }
40005         var items = {
40006             tag : 'div',
40007             cls : 'roo-radio-set-items'
40008         };
40009         
40010         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40011         
40012         if (align === 'left' && this.fieldLabel.length) {
40013             
40014             items = {
40015                 cls : "roo-radio-set-right", 
40016                 cn: [
40017                     items
40018                 ]
40019             };
40020             
40021             if(this.labelWidth > 12){
40022                 label.style = "width: " + this.labelWidth + 'px';
40023             }
40024             
40025             if(this.labelWidth < 13 && this.labelmd == 0){
40026                 this.labelmd = this.labelWidth;
40027             }
40028             
40029             if(this.labellg > 0){
40030                 label.cls += ' col-lg-' + this.labellg;
40031                 items.cls += ' col-lg-' + (12 - this.labellg);
40032             }
40033             
40034             if(this.labelmd > 0){
40035                 label.cls += ' col-md-' + this.labelmd;
40036                 items.cls += ' col-md-' + (12 - this.labelmd);
40037             }
40038             
40039             if(this.labelsm > 0){
40040                 label.cls += ' col-sm-' + this.labelsm;
40041                 items.cls += ' col-sm-' + (12 - this.labelsm);
40042             }
40043             
40044             if(this.labelxs > 0){
40045                 label.cls += ' col-xs-' + this.labelxs;
40046                 items.cls += ' col-xs-' + (12 - this.labelxs);
40047             }
40048         }
40049         
40050         var cfg = {
40051             tag : 'div',
40052             cls : 'roo-radio-set',
40053             cn : [
40054                 {
40055                     tag : 'input',
40056                     cls : 'roo-radio-set-input',
40057                     type : 'hidden',
40058                     name : this.name,
40059                     value : this.value ? this.value :  ''
40060                 },
40061                 label,
40062                 items
40063             ]
40064         };
40065         
40066         if(this.weight.length){
40067             cfg.cls += ' roo-radio-' + this.weight;
40068         }
40069         
40070         if(this.inline) {
40071             cfg.cls += ' roo-radio-set-inline';
40072         }
40073         
40074         var settings=this;
40075         ['xs','sm','md','lg'].map(function(size){
40076             if (settings[size]) {
40077                 cfg.cls += ' col-' + size + '-' + settings[size];
40078             }
40079         });
40080         
40081         return cfg;
40082         
40083     },
40084
40085     initEvents : function()
40086     {
40087         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40088         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40089         
40090         if(!this.fieldLabel.length){
40091             this.labelEl.hide();
40092         }
40093         
40094         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40095         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40096         
40097         this.indicator = this.indicatorEl();
40098         
40099         if(this.indicator){
40100             this.indicator.addClass('invisible');
40101         }
40102         
40103         this.originalValue = this.getValue();
40104         
40105     },
40106     
40107     inputEl: function ()
40108     {
40109         return this.el.select('.roo-radio-set-input', true).first();
40110     },
40111     
40112     getChildContainer : function()
40113     {
40114         return this.itemsEl;
40115     },
40116     
40117     register : function(item)
40118     {
40119         this.radioes.push(item);
40120         
40121     },
40122     
40123     validate : function()
40124     {   
40125         if(this.getVisibilityEl().hasClass('hidden')){
40126             return true;
40127         }
40128         
40129         var valid = false;
40130         
40131         Roo.each(this.radioes, function(i){
40132             if(!i.checked){
40133                 return;
40134             }
40135             
40136             valid = true;
40137             return false;
40138         });
40139         
40140         if(this.allowBlank) {
40141             return true;
40142         }
40143         
40144         if(this.disabled || valid){
40145             this.markValid();
40146             return true;
40147         }
40148         
40149         this.markInvalid();
40150         return false;
40151         
40152     },
40153     
40154     markValid : function()
40155     {
40156         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40157             this.indicatorEl().removeClass('visible');
40158             this.indicatorEl().addClass('invisible');
40159         }
40160         
40161         
40162         if (Roo.bootstrap.version == 3) {
40163             this.el.removeClass([this.invalidClass, this.validClass]);
40164             this.el.addClass(this.validClass);
40165         } else {
40166             this.el.removeClass(['is-invalid','is-valid']);
40167             this.el.addClass(['is-valid']);
40168         }
40169         this.fireEvent('valid', this);
40170     },
40171     
40172     markInvalid : function(msg)
40173     {
40174         if(this.allowBlank || this.disabled){
40175             return;
40176         }
40177         
40178         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40179             this.indicatorEl().removeClass('invisible');
40180             this.indicatorEl().addClass('visible');
40181         }
40182         if (Roo.bootstrap.version == 3) {
40183             this.el.removeClass([this.invalidClass, this.validClass]);
40184             this.el.addClass(this.invalidClass);
40185         } else {
40186             this.el.removeClass(['is-invalid','is-valid']);
40187             this.el.addClass(['is-invalid']);
40188         }
40189         
40190         this.fireEvent('invalid', this, msg);
40191         
40192     },
40193     
40194     setValue : function(v, suppressEvent)
40195     {   
40196         if(this.value === v){
40197             return;
40198         }
40199         
40200         this.value = v;
40201         
40202         if(this.rendered){
40203             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40204         }
40205         
40206         Roo.each(this.radioes, function(i){
40207             i.checked = false;
40208             i.el.removeClass('checked');
40209         });
40210         
40211         Roo.each(this.radioes, function(i){
40212             
40213             if(i.value === v || i.value.toString() === v.toString()){
40214                 i.checked = true;
40215                 i.el.addClass('checked');
40216                 
40217                 if(suppressEvent !== true){
40218                     this.fireEvent('check', this, i);
40219                 }
40220                 
40221                 return false;
40222             }
40223             
40224         }, this);
40225         
40226         this.validate();
40227     },
40228     
40229     clearInvalid : function(){
40230         
40231         if(!this.el || this.preventMark){
40232             return;
40233         }
40234         
40235         this.el.removeClass([this.invalidClass]);
40236         
40237         this.fireEvent('valid', this);
40238     }
40239     
40240 });
40241
40242 Roo.apply(Roo.bootstrap.form.RadioSet, {
40243     
40244     groups: {},
40245     
40246     register : function(set)
40247     {
40248         this.groups[set.name] = set;
40249     },
40250     
40251     get: function(name) 
40252     {
40253         if (typeof(this.groups[name]) == 'undefined') {
40254             return false;
40255         }
40256         
40257         return this.groups[name] ;
40258     }
40259     
40260 });
40261 /*
40262  * Based on:
40263  * Ext JS Library 1.1.1
40264  * Copyright(c) 2006-2007, Ext JS, LLC.
40265  *
40266  * Originally Released Under LGPL - original licence link has changed is not relivant.
40267  *
40268  * Fork - LGPL
40269  * <script type="text/javascript">
40270  */
40271
40272
40273 /**
40274  * @class Roo.bootstrap.SplitBar
40275  * @extends Roo.util.Observable
40276  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40277  * <br><br>
40278  * Usage:
40279  * <pre><code>
40280 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40281                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40282 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40283 split.minSize = 100;
40284 split.maxSize = 600;
40285 split.animate = true;
40286 split.on('moved', splitterMoved);
40287 </code></pre>
40288  * @constructor
40289  * Create a new SplitBar
40290  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40291  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40292  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40293  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40294                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40295                         position of the SplitBar).
40296  */
40297 Roo.bootstrap.SplitBar = function(cfg){
40298     
40299     /** @private */
40300     
40301     //{
40302     //  dragElement : elm
40303     //  resizingElement: el,
40304         // optional..
40305     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40306     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40307         // existingProxy ???
40308     //}
40309     
40310     this.el = Roo.get(cfg.dragElement, true);
40311     this.el.dom.unselectable = "on";
40312     /** @private */
40313     this.resizingEl = Roo.get(cfg.resizingElement, true);
40314
40315     /**
40316      * @private
40317      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40318      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40319      * @type Number
40320      */
40321     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40322     
40323     /**
40324      * The minimum size of the resizing element. (Defaults to 0)
40325      * @type Number
40326      */
40327     this.minSize = 0;
40328     
40329     /**
40330      * The maximum size of the resizing element. (Defaults to 2000)
40331      * @type Number
40332      */
40333     this.maxSize = 2000;
40334     
40335     /**
40336      * Whether to animate the transition to the new size
40337      * @type Boolean
40338      */
40339     this.animate = false;
40340     
40341     /**
40342      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40343      * @type Boolean
40344      */
40345     this.useShim = false;
40346     
40347     /** @private */
40348     this.shim = null;
40349     
40350     if(!cfg.existingProxy){
40351         /** @private */
40352         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40353     }else{
40354         this.proxy = Roo.get(cfg.existingProxy).dom;
40355     }
40356     /** @private */
40357     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40358     
40359     /** @private */
40360     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40361     
40362     /** @private */
40363     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40364     
40365     /** @private */
40366     this.dragSpecs = {};
40367     
40368     /**
40369      * @private The adapter to use to positon and resize elements
40370      */
40371     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40372     this.adapter.init(this);
40373     
40374     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40375         /** @private */
40376         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40377         this.el.addClass("roo-splitbar-h");
40378     }else{
40379         /** @private */
40380         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40381         this.el.addClass("roo-splitbar-v");
40382     }
40383     
40384     this.addEvents({
40385         /**
40386          * @event resize
40387          * Fires when the splitter is moved (alias for {@link #event-moved})
40388          * @param {Roo.bootstrap.SplitBar} this
40389          * @param {Number} newSize the new width or height
40390          */
40391         "resize" : true,
40392         /**
40393          * @event moved
40394          * Fires when the splitter is moved
40395          * @param {Roo.bootstrap.SplitBar} this
40396          * @param {Number} newSize the new width or height
40397          */
40398         "moved" : true,
40399         /**
40400          * @event beforeresize
40401          * Fires before the splitter is dragged
40402          * @param {Roo.bootstrap.SplitBar} this
40403          */
40404         "beforeresize" : true,
40405
40406         "beforeapply" : true
40407     });
40408
40409     Roo.util.Observable.call(this);
40410 };
40411
40412 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40413     onStartProxyDrag : function(x, y){
40414         this.fireEvent("beforeresize", this);
40415         if(!this.overlay){
40416             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40417             o.unselectable();
40418             o.enableDisplayMode("block");
40419             // all splitbars share the same overlay
40420             Roo.bootstrap.SplitBar.prototype.overlay = o;
40421         }
40422         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40423         this.overlay.show();
40424         Roo.get(this.proxy).setDisplayed("block");
40425         var size = this.adapter.getElementSize(this);
40426         this.activeMinSize = this.getMinimumSize();;
40427         this.activeMaxSize = this.getMaximumSize();;
40428         var c1 = size - this.activeMinSize;
40429         var c2 = Math.max(this.activeMaxSize - size, 0);
40430         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40431             this.dd.resetConstraints();
40432             this.dd.setXConstraint(
40433                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40434                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40435             );
40436             this.dd.setYConstraint(0, 0);
40437         }else{
40438             this.dd.resetConstraints();
40439             this.dd.setXConstraint(0, 0);
40440             this.dd.setYConstraint(
40441                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40442                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40443             );
40444          }
40445         this.dragSpecs.startSize = size;
40446         this.dragSpecs.startPoint = [x, y];
40447         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40448     },
40449     
40450     /** 
40451      * @private Called after the drag operation by the DDProxy
40452      */
40453     onEndProxyDrag : function(e){
40454         Roo.get(this.proxy).setDisplayed(false);
40455         var endPoint = Roo.lib.Event.getXY(e);
40456         if(this.overlay){
40457             this.overlay.hide();
40458         }
40459         var newSize;
40460         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40461             newSize = this.dragSpecs.startSize + 
40462                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40463                     endPoint[0] - this.dragSpecs.startPoint[0] :
40464                     this.dragSpecs.startPoint[0] - endPoint[0]
40465                 );
40466         }else{
40467             newSize = this.dragSpecs.startSize + 
40468                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40469                     endPoint[1] - this.dragSpecs.startPoint[1] :
40470                     this.dragSpecs.startPoint[1] - endPoint[1]
40471                 );
40472         }
40473         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40474         if(newSize != this.dragSpecs.startSize){
40475             if(this.fireEvent('beforeapply', this, newSize) !== false){
40476                 this.adapter.setElementSize(this, newSize);
40477                 this.fireEvent("moved", this, newSize);
40478                 this.fireEvent("resize", this, newSize);
40479             }
40480         }
40481     },
40482     
40483     /**
40484      * Get the adapter this SplitBar uses
40485      * @return The adapter object
40486      */
40487     getAdapter : function(){
40488         return this.adapter;
40489     },
40490     
40491     /**
40492      * Set the adapter this SplitBar uses
40493      * @param {Object} adapter A SplitBar adapter object
40494      */
40495     setAdapter : function(adapter){
40496         this.adapter = adapter;
40497         this.adapter.init(this);
40498     },
40499     
40500     /**
40501      * Gets the minimum size for the resizing element
40502      * @return {Number} The minimum size
40503      */
40504     getMinimumSize : function(){
40505         return this.minSize;
40506     },
40507     
40508     /**
40509      * Sets the minimum size for the resizing element
40510      * @param {Number} minSize The minimum size
40511      */
40512     setMinimumSize : function(minSize){
40513         this.minSize = minSize;
40514     },
40515     
40516     /**
40517      * Gets the maximum size for the resizing element
40518      * @return {Number} The maximum size
40519      */
40520     getMaximumSize : function(){
40521         return this.maxSize;
40522     },
40523     
40524     /**
40525      * Sets the maximum size for the resizing element
40526      * @param {Number} maxSize The maximum size
40527      */
40528     setMaximumSize : function(maxSize){
40529         this.maxSize = maxSize;
40530     },
40531     
40532     /**
40533      * Sets the initialize size for the resizing element
40534      * @param {Number} size The initial size
40535      */
40536     setCurrentSize : function(size){
40537         var oldAnimate = this.animate;
40538         this.animate = false;
40539         this.adapter.setElementSize(this, size);
40540         this.animate = oldAnimate;
40541     },
40542     
40543     /**
40544      * Destroy this splitbar. 
40545      * @param {Boolean} removeEl True to remove the element
40546      */
40547     destroy : function(removeEl){
40548         if(this.shim){
40549             this.shim.remove();
40550         }
40551         this.dd.unreg();
40552         this.proxy.parentNode.removeChild(this.proxy);
40553         if(removeEl){
40554             this.el.remove();
40555         }
40556     }
40557 });
40558
40559 /**
40560  * @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.
40561  */
40562 Roo.bootstrap.SplitBar.createProxy = function(dir){
40563     var proxy = new Roo.Element(document.createElement("div"));
40564     proxy.unselectable();
40565     var cls = 'roo-splitbar-proxy';
40566     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40567     document.body.appendChild(proxy.dom);
40568     return proxy.dom;
40569 };
40570
40571 /** 
40572  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40573  * Default Adapter. It assumes the splitter and resizing element are not positioned
40574  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40575  */
40576 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40577 };
40578
40579 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40580     // do nothing for now
40581     init : function(s){
40582     
40583     },
40584     /**
40585      * Called before drag operations to get the current size of the resizing element. 
40586      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40587      */
40588      getElementSize : function(s){
40589         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40590             return s.resizingEl.getWidth();
40591         }else{
40592             return s.resizingEl.getHeight();
40593         }
40594     },
40595     
40596     /**
40597      * Called after drag operations to set the size of the resizing element.
40598      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40599      * @param {Number} newSize The new size to set
40600      * @param {Function} onComplete A function to be invoked when resizing is complete
40601      */
40602     setElementSize : function(s, newSize, onComplete){
40603         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40604             if(!s.animate){
40605                 s.resizingEl.setWidth(newSize);
40606                 if(onComplete){
40607                     onComplete(s, newSize);
40608                 }
40609             }else{
40610                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40611             }
40612         }else{
40613             
40614             if(!s.animate){
40615                 s.resizingEl.setHeight(newSize);
40616                 if(onComplete){
40617                     onComplete(s, newSize);
40618                 }
40619             }else{
40620                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40621             }
40622         }
40623     }
40624 };
40625
40626 /** 
40627  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40628  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40629  * Adapter that  moves the splitter element to align with the resized sizing element. 
40630  * Used with an absolute positioned SplitBar.
40631  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40632  * document.body, make sure you assign an id to the body element.
40633  */
40634 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40635     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40636     this.container = Roo.get(container);
40637 };
40638
40639 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40640     init : function(s){
40641         this.basic.init(s);
40642     },
40643     
40644     getElementSize : function(s){
40645         return this.basic.getElementSize(s);
40646     },
40647     
40648     setElementSize : function(s, newSize, onComplete){
40649         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40650     },
40651     
40652     moveSplitter : function(s){
40653         var yes = Roo.bootstrap.SplitBar;
40654         switch(s.placement){
40655             case yes.LEFT:
40656                 s.el.setX(s.resizingEl.getRight());
40657                 break;
40658             case yes.RIGHT:
40659                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40660                 break;
40661             case yes.TOP:
40662                 s.el.setY(s.resizingEl.getBottom());
40663                 break;
40664             case yes.BOTTOM:
40665                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40666                 break;
40667         }
40668     }
40669 };
40670
40671 /**
40672  * Orientation constant - Create a vertical SplitBar
40673  * @static
40674  * @type Number
40675  */
40676 Roo.bootstrap.SplitBar.VERTICAL = 1;
40677
40678 /**
40679  * Orientation constant - Create a horizontal SplitBar
40680  * @static
40681  * @type Number
40682  */
40683 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40684
40685 /**
40686  * Placement constant - The resizing element is to the left of the splitter element
40687  * @static
40688  * @type Number
40689  */
40690 Roo.bootstrap.SplitBar.LEFT = 1;
40691
40692 /**
40693  * Placement constant - The resizing element is to the right of the splitter element
40694  * @static
40695  * @type Number
40696  */
40697 Roo.bootstrap.SplitBar.RIGHT = 2;
40698
40699 /**
40700  * Placement constant - The resizing element is positioned above the splitter element
40701  * @static
40702  * @type Number
40703  */
40704 Roo.bootstrap.SplitBar.TOP = 3;
40705
40706 /**
40707  * Placement constant - The resizing element is positioned under splitter element
40708  * @static
40709  * @type Number
40710  */
40711 Roo.bootstrap.SplitBar.BOTTOM = 4;
40712 /*
40713  * Based on:
40714  * Ext JS Library 1.1.1
40715  * Copyright(c) 2006-2007, Ext JS, LLC.
40716  *
40717  * Originally Released Under LGPL - original licence link has changed is not relivant.
40718  *
40719  * Fork - LGPL
40720  * <script type="text/javascript">
40721  */
40722
40723 /**
40724  * @class Roo.bootstrap.layout.Manager
40725  * @extends Roo.bootstrap.Component
40726  * @abstract
40727  * Base class for layout managers.
40728  */
40729 Roo.bootstrap.layout.Manager = function(config)
40730 {
40731     this.monitorWindowResize = true; // do this before we apply configuration.
40732     
40733     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40734
40735
40736
40737
40738
40739     /** false to disable window resize monitoring @type Boolean */
40740     
40741     this.regions = {};
40742     this.addEvents({
40743         /**
40744          * @event layout
40745          * Fires when a layout is performed.
40746          * @param {Roo.LayoutManager} this
40747          */
40748         "layout" : true,
40749         /**
40750          * @event regionresized
40751          * Fires when the user resizes a region.
40752          * @param {Roo.LayoutRegion} region The resized region
40753          * @param {Number} newSize The new size (width for east/west, height for north/south)
40754          */
40755         "regionresized" : true,
40756         /**
40757          * @event regioncollapsed
40758          * Fires when a region is collapsed.
40759          * @param {Roo.LayoutRegion} region The collapsed region
40760          */
40761         "regioncollapsed" : true,
40762         /**
40763          * @event regionexpanded
40764          * Fires when a region is expanded.
40765          * @param {Roo.LayoutRegion} region The expanded region
40766          */
40767         "regionexpanded" : true
40768     });
40769     this.updating = false;
40770
40771     if (config.el) {
40772         this.el = Roo.get(config.el);
40773         this.initEvents();
40774     }
40775
40776 };
40777
40778 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40779
40780
40781     regions : null,
40782
40783     monitorWindowResize : true,
40784
40785
40786     updating : false,
40787
40788
40789     onRender : function(ct, position)
40790     {
40791         if(!this.el){
40792             this.el = Roo.get(ct);
40793             this.initEvents();
40794         }
40795         //this.fireEvent('render',this);
40796     },
40797
40798
40799     initEvents: function()
40800     {
40801
40802
40803         // ie scrollbar fix
40804         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40805             document.body.scroll = "no";
40806         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40807             this.el.position('relative');
40808         }
40809         this.id = this.el.id;
40810         this.el.addClass("roo-layout-container");
40811         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40812         if(this.el.dom != document.body ) {
40813             this.el.on('resize', this.layout,this);
40814             this.el.on('show', this.layout,this);
40815         }
40816
40817     },
40818
40819     /**
40820      * Returns true if this layout is currently being updated
40821      * @return {Boolean}
40822      */
40823     isUpdating : function(){
40824         return this.updating;
40825     },
40826
40827     /**
40828      * Suspend the LayoutManager from doing auto-layouts while
40829      * making multiple add or remove calls
40830      */
40831     beginUpdate : function(){
40832         this.updating = true;
40833     },
40834
40835     /**
40836      * Restore auto-layouts and optionally disable the manager from performing a layout
40837      * @param {Boolean} noLayout true to disable a layout update
40838      */
40839     endUpdate : function(noLayout){
40840         this.updating = false;
40841         if(!noLayout){
40842             this.layout();
40843         }
40844     },
40845
40846     layout: function(){
40847         // abstract...
40848     },
40849
40850     onRegionResized : function(region, newSize){
40851         this.fireEvent("regionresized", region, newSize);
40852         this.layout();
40853     },
40854
40855     onRegionCollapsed : function(region){
40856         this.fireEvent("regioncollapsed", region);
40857     },
40858
40859     onRegionExpanded : function(region){
40860         this.fireEvent("regionexpanded", region);
40861     },
40862
40863     /**
40864      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40865      * performs box-model adjustments.
40866      * @return {Object} The size as an object {width: (the width), height: (the height)}
40867      */
40868     getViewSize : function()
40869     {
40870         var size;
40871         if(this.el.dom != document.body){
40872             size = this.el.getSize();
40873         }else{
40874             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40875         }
40876         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40877         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40878         return size;
40879     },
40880
40881     /**
40882      * Returns the Element this layout is bound to.
40883      * @return {Roo.Element}
40884      */
40885     getEl : function(){
40886         return this.el;
40887     },
40888
40889     /**
40890      * Returns the specified region.
40891      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40892      * @return {Roo.LayoutRegion}
40893      */
40894     getRegion : function(target){
40895         return this.regions[target.toLowerCase()];
40896     },
40897
40898     onWindowResize : function(){
40899         if(this.monitorWindowResize){
40900             this.layout();
40901         }
40902     }
40903 });
40904 /*
40905  * Based on:
40906  * Ext JS Library 1.1.1
40907  * Copyright(c) 2006-2007, Ext JS, LLC.
40908  *
40909  * Originally Released Under LGPL - original licence link has changed is not relivant.
40910  *
40911  * Fork - LGPL
40912  * <script type="text/javascript">
40913  */
40914 /**
40915  * @class Roo.bootstrap.layout.Border
40916  * @extends Roo.bootstrap.layout.Manager
40917  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40918  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40919  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40920  * please see: examples/bootstrap/nested.html<br><br>
40921  
40922 <b>The container the layout is rendered into can be either the body element or any other element.
40923 If it is not the body element, the container needs to either be an absolute positioned element,
40924 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40925 the container size if it is not the body element.</b>
40926
40927 * @constructor
40928 * Create a new Border
40929 * @param {Object} config Configuration options
40930  */
40931 Roo.bootstrap.layout.Border = function(config){
40932     config = config || {};
40933     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40934     
40935     
40936     
40937     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40938         if(config[region]){
40939             config[region].region = region;
40940             this.addRegion(config[region]);
40941         }
40942     },this);
40943     
40944 };
40945
40946 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40947
40948 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40949     
40950         /**
40951          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40952          */
40953         /**
40954          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40955          */
40956         /**
40957          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40958          */
40959         /**
40960          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40961          */
40962         /**
40963          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40964          */
40965         
40966         
40967         
40968         
40969     parent : false, // this might point to a 'nest' or a ???
40970     
40971     /**
40972      * Creates and adds a new region if it doesn't already exist.
40973      * @param {String} target The target region key (north, south, east, west or center).
40974      * @param {Object} config The regions config object
40975      * @return {BorderLayoutRegion} The new region
40976      */
40977     addRegion : function(config)
40978     {
40979         if(!this.regions[config.region]){
40980             var r = this.factory(config);
40981             this.bindRegion(r);
40982         }
40983         return this.regions[config.region];
40984     },
40985
40986     // private (kinda)
40987     bindRegion : function(r){
40988         this.regions[r.config.region] = r;
40989         
40990         r.on("visibilitychange",    this.layout, this);
40991         r.on("paneladded",          this.layout, this);
40992         r.on("panelremoved",        this.layout, this);
40993         r.on("invalidated",         this.layout, this);
40994         r.on("resized",             this.onRegionResized, this);
40995         r.on("collapsed",           this.onRegionCollapsed, this);
40996         r.on("expanded",            this.onRegionExpanded, this);
40997     },
40998
40999     /**
41000      * Performs a layout update.
41001      */
41002     layout : function()
41003     {
41004         if(this.updating) {
41005             return;
41006         }
41007         
41008         // render all the rebions if they have not been done alreayd?
41009         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41010             if(this.regions[region] && !this.regions[region].bodyEl){
41011                 this.regions[region].onRender(this.el)
41012             }
41013         },this);
41014         
41015         var size = this.getViewSize();
41016         var w = size.width;
41017         var h = size.height;
41018         var centerW = w;
41019         var centerH = h;
41020         var centerY = 0;
41021         var centerX = 0;
41022         //var x = 0, y = 0;
41023
41024         var rs = this.regions;
41025         var north = rs["north"];
41026         var south = rs["south"]; 
41027         var west = rs["west"];
41028         var east = rs["east"];
41029         var center = rs["center"];
41030         //if(this.hideOnLayout){ // not supported anymore
41031             //c.el.setStyle("display", "none");
41032         //}
41033         if(north && north.isVisible()){
41034             var b = north.getBox();
41035             var m = north.getMargins();
41036             b.width = w - (m.left+m.right);
41037             b.x = m.left;
41038             b.y = m.top;
41039             centerY = b.height + b.y + m.bottom;
41040             centerH -= centerY;
41041             north.updateBox(this.safeBox(b));
41042         }
41043         if(south && south.isVisible()){
41044             var b = south.getBox();
41045             var m = south.getMargins();
41046             b.width = w - (m.left+m.right);
41047             b.x = m.left;
41048             var totalHeight = (b.height + m.top + m.bottom);
41049             b.y = h - totalHeight + m.top;
41050             centerH -= totalHeight;
41051             south.updateBox(this.safeBox(b));
41052         }
41053         if(west && west.isVisible()){
41054             var b = west.getBox();
41055             var m = west.getMargins();
41056             b.height = centerH - (m.top+m.bottom);
41057             b.x = m.left;
41058             b.y = centerY + m.top;
41059             var totalWidth = (b.width + m.left + m.right);
41060             centerX += totalWidth;
41061             centerW -= totalWidth;
41062             west.updateBox(this.safeBox(b));
41063         }
41064         if(east && east.isVisible()){
41065             var b = east.getBox();
41066             var m = east.getMargins();
41067             b.height = centerH - (m.top+m.bottom);
41068             var totalWidth = (b.width + m.left + m.right);
41069             b.x = w - totalWidth + m.left;
41070             b.y = centerY + m.top;
41071             centerW -= totalWidth;
41072             east.updateBox(this.safeBox(b));
41073         }
41074         if(center){
41075             var m = center.getMargins();
41076             var centerBox = {
41077                 x: centerX + m.left,
41078                 y: centerY + m.top,
41079                 width: centerW - (m.left+m.right),
41080                 height: centerH - (m.top+m.bottom)
41081             };
41082             //if(this.hideOnLayout){
41083                 //center.el.setStyle("display", "block");
41084             //}
41085             center.updateBox(this.safeBox(centerBox));
41086         }
41087         this.el.repaint();
41088         this.fireEvent("layout", this);
41089     },
41090
41091     // private
41092     safeBox : function(box){
41093         box.width = Math.max(0, box.width);
41094         box.height = Math.max(0, box.height);
41095         return box;
41096     },
41097
41098     /**
41099      * Adds a ContentPanel (or subclass) to this layout.
41100      * @param {String} target The target region key (north, south, east, west or center).
41101      * @param {Roo.ContentPanel} panel The panel to add
41102      * @return {Roo.ContentPanel} The added panel
41103      */
41104     add : function(target, panel){
41105          
41106         target = target.toLowerCase();
41107         return this.regions[target].add(panel);
41108     },
41109
41110     /**
41111      * Remove a ContentPanel (or subclass) to this layout.
41112      * @param {String} target The target region key (north, south, east, west or center).
41113      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41114      * @return {Roo.ContentPanel} The removed panel
41115      */
41116     remove : function(target, panel){
41117         target = target.toLowerCase();
41118         return this.regions[target].remove(panel);
41119     },
41120
41121     /**
41122      * Searches all regions for a panel with the specified id
41123      * @param {String} panelId
41124      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41125      */
41126     findPanel : function(panelId){
41127         var rs = this.regions;
41128         for(var target in rs){
41129             if(typeof rs[target] != "function"){
41130                 var p = rs[target].getPanel(panelId);
41131                 if(p){
41132                     return p;
41133                 }
41134             }
41135         }
41136         return null;
41137     },
41138
41139     /**
41140      * Searches all regions for a panel with the specified id and activates (shows) it.
41141      * @param {String/ContentPanel} panelId The panels id or the panel itself
41142      * @return {Roo.ContentPanel} The shown panel or null
41143      */
41144     showPanel : function(panelId) {
41145       var rs = this.regions;
41146       for(var target in rs){
41147          var r = rs[target];
41148          if(typeof r != "function"){
41149             if(r.hasPanel(panelId)){
41150                return r.showPanel(panelId);
41151             }
41152          }
41153       }
41154       return null;
41155    },
41156
41157    /**
41158      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41159      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41160      */
41161    /*
41162     restoreState : function(provider){
41163         if(!provider){
41164             provider = Roo.state.Manager;
41165         }
41166         var sm = new Roo.LayoutStateManager();
41167         sm.init(this, provider);
41168     },
41169 */
41170  
41171  
41172     /**
41173      * Adds a xtype elements to the layout.
41174      * <pre><code>
41175
41176 layout.addxtype({
41177        xtype : 'ContentPanel',
41178        region: 'west',
41179        items: [ .... ]
41180    }
41181 );
41182
41183 layout.addxtype({
41184         xtype : 'NestedLayoutPanel',
41185         region: 'west',
41186         layout: {
41187            center: { },
41188            west: { }   
41189         },
41190         items : [ ... list of content panels or nested layout panels.. ]
41191    }
41192 );
41193 </code></pre>
41194      * @param {Object} cfg Xtype definition of item to add.
41195      */
41196     addxtype : function(cfg)
41197     {
41198         // basically accepts a pannel...
41199         // can accept a layout region..!?!?
41200         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41201         
41202         
41203         // theory?  children can only be panels??
41204         
41205         //if (!cfg.xtype.match(/Panel$/)) {
41206         //    return false;
41207         //}
41208         var ret = false;
41209         
41210         if (typeof(cfg.region) == 'undefined') {
41211             Roo.log("Failed to add Panel, region was not set");
41212             Roo.log(cfg);
41213             return false;
41214         }
41215         var region = cfg.region;
41216         delete cfg.region;
41217         
41218           
41219         var xitems = [];
41220         if (cfg.items) {
41221             xitems = cfg.items;
41222             delete cfg.items;
41223         }
41224         var nb = false;
41225         
41226         if ( region == 'center') {
41227             Roo.log("Center: " + cfg.title);
41228         }
41229         
41230         
41231         switch(cfg.xtype) 
41232         {
41233             case 'Content':  // ContentPanel (el, cfg)
41234             case 'Scroll':  // ContentPanel (el, cfg)
41235             case 'View': 
41236                 cfg.autoCreate = cfg.autoCreate || true;
41237                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41238                 //} else {
41239                 //    var el = this.el.createChild();
41240                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41241                 //}
41242                 
41243                 this.add(region, ret);
41244                 break;
41245             
41246             /*
41247             case 'TreePanel': // our new panel!
41248                 cfg.el = this.el.createChild();
41249                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41250                 this.add(region, ret);
41251                 break;
41252             */
41253             
41254             case 'Nest': 
41255                 // create a new Layout (which is  a Border Layout...
41256                 
41257                 var clayout = cfg.layout;
41258                 clayout.el  = this.el.createChild();
41259                 clayout.items   = clayout.items  || [];
41260                 
41261                 delete cfg.layout;
41262                 
41263                 // replace this exitems with the clayout ones..
41264                 xitems = clayout.items;
41265                  
41266                 // force background off if it's in center...
41267                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41268                     cfg.background = false;
41269                 }
41270                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41271                 
41272                 
41273                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41274                 //console.log('adding nested layout panel '  + cfg.toSource());
41275                 this.add(region, ret);
41276                 nb = {}; /// find first...
41277                 break;
41278             
41279             case 'Grid':
41280                 
41281                 // needs grid and region
41282                 
41283                 //var el = this.getRegion(region).el.createChild();
41284                 /*
41285                  *var el = this.el.createChild();
41286                 // create the grid first...
41287                 cfg.grid.container = el;
41288                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41289                 */
41290                 
41291                 if (region == 'center' && this.active ) {
41292                     cfg.background = false;
41293                 }
41294                 
41295                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41296                 
41297                 this.add(region, ret);
41298                 /*
41299                 if (cfg.background) {
41300                     // render grid on panel activation (if panel background)
41301                     ret.on('activate', function(gp) {
41302                         if (!gp.grid.rendered) {
41303                     //        gp.grid.render(el);
41304                         }
41305                     });
41306                 } else {
41307                   //  cfg.grid.render(el);
41308                 }
41309                 */
41310                 break;
41311            
41312            
41313             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41314                 // it was the old xcomponent building that caused this before.
41315                 // espeically if border is the top element in the tree.
41316                 ret = this;
41317                 break; 
41318                 
41319                     
41320                 
41321                 
41322                 
41323             default:
41324                 /*
41325                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41326                     
41327                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41328                     this.add(region, ret);
41329                 } else {
41330                 */
41331                     Roo.log(cfg);
41332                     throw "Can not add '" + cfg.xtype + "' to Border";
41333                     return null;
41334              
41335                                 
41336              
41337         }
41338         this.beginUpdate();
41339         // add children..
41340         var region = '';
41341         var abn = {};
41342         Roo.each(xitems, function(i)  {
41343             region = nb && i.region ? i.region : false;
41344             
41345             var add = ret.addxtype(i);
41346            
41347             if (region) {
41348                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41349                 if (!i.background) {
41350                     abn[region] = nb[region] ;
41351                 }
41352             }
41353             
41354         });
41355         this.endUpdate();
41356
41357         // make the last non-background panel active..
41358         //if (nb) { Roo.log(abn); }
41359         if (nb) {
41360             
41361             for(var r in abn) {
41362                 region = this.getRegion(r);
41363                 if (region) {
41364                     // tried using nb[r], but it does not work..
41365                      
41366                     region.showPanel(abn[r]);
41367                    
41368                 }
41369             }
41370         }
41371         return ret;
41372         
41373     },
41374     
41375     
41376 // private
41377     factory : function(cfg)
41378     {
41379         
41380         var validRegions = Roo.bootstrap.layout.Border.regions;
41381
41382         var target = cfg.region;
41383         cfg.mgr = this;
41384         
41385         var r = Roo.bootstrap.layout;
41386         Roo.log(target);
41387         switch(target){
41388             case "north":
41389                 return new r.North(cfg);
41390             case "south":
41391                 return new r.South(cfg);
41392             case "east":
41393                 return new r.East(cfg);
41394             case "west":
41395                 return new r.West(cfg);
41396             case "center":
41397                 return new r.Center(cfg);
41398         }
41399         throw 'Layout region "'+target+'" not supported.';
41400     }
41401     
41402     
41403 });
41404  /*
41405  * Based on:
41406  * Ext JS Library 1.1.1
41407  * Copyright(c) 2006-2007, Ext JS, LLC.
41408  *
41409  * Originally Released Under LGPL - original licence link has changed is not relivant.
41410  *
41411  * Fork - LGPL
41412  * <script type="text/javascript">
41413  */
41414  
41415 /**
41416  * @class Roo.bootstrap.layout.Basic
41417  * @extends Roo.util.Observable
41418  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41419  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41420  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41421  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41422  * @cfg {string}   region  the region that it inhabits..
41423  * @cfg {bool}   skipConfig skip config?
41424  * 
41425
41426  */
41427 Roo.bootstrap.layout.Basic = function(config){
41428     
41429     this.mgr = config.mgr;
41430     
41431     this.position = config.region;
41432     
41433     var skipConfig = config.skipConfig;
41434     
41435     this.events = {
41436         /**
41437          * @scope Roo.BasicLayoutRegion
41438          */
41439         
41440         /**
41441          * @event beforeremove
41442          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41443          * @param {Roo.LayoutRegion} this
41444          * @param {Roo.ContentPanel} panel The panel
41445          * @param {Object} e The cancel event object
41446          */
41447         "beforeremove" : true,
41448         /**
41449          * @event invalidated
41450          * Fires when the layout for this region is changed.
41451          * @param {Roo.LayoutRegion} this
41452          */
41453         "invalidated" : true,
41454         /**
41455          * @event visibilitychange
41456          * Fires when this region is shown or hidden 
41457          * @param {Roo.LayoutRegion} this
41458          * @param {Boolean} visibility true or false
41459          */
41460         "visibilitychange" : true,
41461         /**
41462          * @event paneladded
41463          * Fires when a panel is added. 
41464          * @param {Roo.LayoutRegion} this
41465          * @param {Roo.ContentPanel} panel The panel
41466          */
41467         "paneladded" : true,
41468         /**
41469          * @event panelremoved
41470          * Fires when a panel is removed. 
41471          * @param {Roo.LayoutRegion} this
41472          * @param {Roo.ContentPanel} panel The panel
41473          */
41474         "panelremoved" : true,
41475         /**
41476          * @event beforecollapse
41477          * Fires when this region before collapse.
41478          * @param {Roo.LayoutRegion} this
41479          */
41480         "beforecollapse" : true,
41481         /**
41482          * @event collapsed
41483          * Fires when this region is collapsed.
41484          * @param {Roo.LayoutRegion} this
41485          */
41486         "collapsed" : true,
41487         /**
41488          * @event expanded
41489          * Fires when this region is expanded.
41490          * @param {Roo.LayoutRegion} this
41491          */
41492         "expanded" : true,
41493         /**
41494          * @event slideshow
41495          * Fires when this region is slid into view.
41496          * @param {Roo.LayoutRegion} this
41497          */
41498         "slideshow" : true,
41499         /**
41500          * @event slidehide
41501          * Fires when this region slides out of view. 
41502          * @param {Roo.LayoutRegion} this
41503          */
41504         "slidehide" : true,
41505         /**
41506          * @event panelactivated
41507          * Fires when a panel is activated. 
41508          * @param {Roo.LayoutRegion} this
41509          * @param {Roo.ContentPanel} panel The activated panel
41510          */
41511         "panelactivated" : true,
41512         /**
41513          * @event resized
41514          * Fires when the user resizes this region. 
41515          * @param {Roo.LayoutRegion} this
41516          * @param {Number} newSize The new size (width for east/west, height for north/south)
41517          */
41518         "resized" : true
41519     };
41520     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41521     this.panels = new Roo.util.MixedCollection();
41522     this.panels.getKey = this.getPanelId.createDelegate(this);
41523     this.box = null;
41524     this.activePanel = null;
41525     // ensure listeners are added...
41526     
41527     if (config.listeners || config.events) {
41528         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41529             listeners : config.listeners || {},
41530             events : config.events || {}
41531         });
41532     }
41533     
41534     if(skipConfig !== true){
41535         this.applyConfig(config);
41536     }
41537 };
41538
41539 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41540 {
41541     getPanelId : function(p){
41542         return p.getId();
41543     },
41544     
41545     applyConfig : function(config){
41546         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41547         this.config = config;
41548         
41549     },
41550     
41551     /**
41552      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41553      * the width, for horizontal (north, south) the height.
41554      * @param {Number} newSize The new width or height
41555      */
41556     resizeTo : function(newSize){
41557         var el = this.el ? this.el :
41558                  (this.activePanel ? this.activePanel.getEl() : null);
41559         if(el){
41560             switch(this.position){
41561                 case "east":
41562                 case "west":
41563                     el.setWidth(newSize);
41564                     this.fireEvent("resized", this, newSize);
41565                 break;
41566                 case "north":
41567                 case "south":
41568                     el.setHeight(newSize);
41569                     this.fireEvent("resized", this, newSize);
41570                 break;                
41571             }
41572         }
41573     },
41574     
41575     getBox : function(){
41576         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41577     },
41578     
41579     getMargins : function(){
41580         return this.margins;
41581     },
41582     
41583     updateBox : function(box){
41584         this.box = box;
41585         var el = this.activePanel.getEl();
41586         el.dom.style.left = box.x + "px";
41587         el.dom.style.top = box.y + "px";
41588         this.activePanel.setSize(box.width, box.height);
41589     },
41590     
41591     /**
41592      * Returns the container element for this region.
41593      * @return {Roo.Element}
41594      */
41595     getEl : function(){
41596         return this.activePanel;
41597     },
41598     
41599     /**
41600      * Returns true if this region is currently visible.
41601      * @return {Boolean}
41602      */
41603     isVisible : function(){
41604         return this.activePanel ? true : false;
41605     },
41606     
41607     setActivePanel : function(panel){
41608         panel = this.getPanel(panel);
41609         if(this.activePanel && this.activePanel != panel){
41610             this.activePanel.setActiveState(false);
41611             this.activePanel.getEl().setLeftTop(-10000,-10000);
41612         }
41613         this.activePanel = panel;
41614         panel.setActiveState(true);
41615         if(this.box){
41616             panel.setSize(this.box.width, this.box.height);
41617         }
41618         this.fireEvent("panelactivated", this, panel);
41619         this.fireEvent("invalidated");
41620     },
41621     
41622     /**
41623      * Show the specified panel.
41624      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41625      * @return {Roo.ContentPanel} The shown panel or null
41626      */
41627     showPanel : function(panel){
41628         panel = this.getPanel(panel);
41629         if(panel){
41630             this.setActivePanel(panel);
41631         }
41632         return panel;
41633     },
41634     
41635     /**
41636      * Get the active panel for this region.
41637      * @return {Roo.ContentPanel} The active panel or null
41638      */
41639     getActivePanel : function(){
41640         return this.activePanel;
41641     },
41642     
41643     /**
41644      * Add the passed ContentPanel(s)
41645      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41646      * @return {Roo.ContentPanel} The panel added (if only one was added)
41647      */
41648     add : function(panel){
41649         if(arguments.length > 1){
41650             for(var i = 0, len = arguments.length; i < len; i++) {
41651                 this.add(arguments[i]);
41652             }
41653             return null;
41654         }
41655         if(this.hasPanel(panel)){
41656             this.showPanel(panel);
41657             return panel;
41658         }
41659         var el = panel.getEl();
41660         if(el.dom.parentNode != this.mgr.el.dom){
41661             this.mgr.el.dom.appendChild(el.dom);
41662         }
41663         if(panel.setRegion){
41664             panel.setRegion(this);
41665         }
41666         this.panels.add(panel);
41667         el.setStyle("position", "absolute");
41668         if(!panel.background){
41669             this.setActivePanel(panel);
41670             if(this.config.initialSize && this.panels.getCount()==1){
41671                 this.resizeTo(this.config.initialSize);
41672             }
41673         }
41674         this.fireEvent("paneladded", this, panel);
41675         return panel;
41676     },
41677     
41678     /**
41679      * Returns true if the panel is in this region.
41680      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41681      * @return {Boolean}
41682      */
41683     hasPanel : function(panel){
41684         if(typeof panel == "object"){ // must be panel obj
41685             panel = panel.getId();
41686         }
41687         return this.getPanel(panel) ? true : false;
41688     },
41689     
41690     /**
41691      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41692      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41693      * @param {Boolean} preservePanel Overrides the config preservePanel option
41694      * @return {Roo.ContentPanel} The panel that was removed
41695      */
41696     remove : function(panel, preservePanel){
41697         panel = this.getPanel(panel);
41698         if(!panel){
41699             return null;
41700         }
41701         var e = {};
41702         this.fireEvent("beforeremove", this, panel, e);
41703         if(e.cancel === true){
41704             return null;
41705         }
41706         var panelId = panel.getId();
41707         this.panels.removeKey(panelId);
41708         return panel;
41709     },
41710     
41711     /**
41712      * Returns the panel specified or null if it's not in this region.
41713      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41714      * @return {Roo.ContentPanel}
41715      */
41716     getPanel : function(id){
41717         if(typeof id == "object"){ // must be panel obj
41718             return id;
41719         }
41720         return this.panels.get(id);
41721     },
41722     
41723     /**
41724      * Returns this regions position (north/south/east/west/center).
41725      * @return {String} 
41726      */
41727     getPosition: function(){
41728         return this.position;    
41729     }
41730 });/*
41731  * Based on:
41732  * Ext JS Library 1.1.1
41733  * Copyright(c) 2006-2007, Ext JS, LLC.
41734  *
41735  * Originally Released Under LGPL - original licence link has changed is not relivant.
41736  *
41737  * Fork - LGPL
41738  * <script type="text/javascript">
41739  */
41740  
41741 /**
41742  * @class Roo.bootstrap.layout.Region
41743  * @extends Roo.bootstrap.layout.Basic
41744  * This class represents a region in a layout manager.
41745  
41746  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41747  * @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})
41748  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41749  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41750  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41751  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41752  * @cfg {String}    title           The title for the region (overrides panel titles)
41753  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41754  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41755  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41756  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41757  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41758  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41759  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41760  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41761  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41762  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41763
41764  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41765  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41766  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41767  * @cfg {Number}    width           For East/West panels
41768  * @cfg {Number}    height          For North/South panels
41769  * @cfg {Boolean}   split           To show the splitter
41770  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41771  * 
41772  * @cfg {string}   cls             Extra CSS classes to add to region
41773  * 
41774  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41775  * @cfg {string}   region  the region that it inhabits..
41776  *
41777
41778  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41779  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41780
41781  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41782  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41783  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41784  */
41785 Roo.bootstrap.layout.Region = function(config)
41786 {
41787     this.applyConfig(config);
41788
41789     var mgr = config.mgr;
41790     var pos = config.region;
41791     config.skipConfig = true;
41792     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41793     
41794     if (mgr.el) {
41795         this.onRender(mgr.el);   
41796     }
41797      
41798     this.visible = true;
41799     this.collapsed = false;
41800     this.unrendered_panels = [];
41801 };
41802
41803 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41804
41805     position: '', // set by wrapper (eg. north/south etc..)
41806     unrendered_panels : null,  // unrendered panels.
41807     
41808     tabPosition : false,
41809     
41810     mgr: false, // points to 'Border'
41811     
41812     
41813     createBody : function(){
41814         /** This region's body element 
41815         * @type Roo.Element */
41816         this.bodyEl = this.el.createChild({
41817                 tag: "div",
41818                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41819         });
41820     },
41821
41822     onRender: function(ctr, pos)
41823     {
41824         var dh = Roo.DomHelper;
41825         /** This region's container element 
41826         * @type Roo.Element */
41827         this.el = dh.append(ctr.dom, {
41828                 tag: "div",
41829                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41830             }, true);
41831         /** This region's title element 
41832         * @type Roo.Element */
41833     
41834         this.titleEl = dh.append(this.el.dom,  {
41835                 tag: "div",
41836                 unselectable: "on",
41837                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41838                 children:[
41839                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41840                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41841                 ]
41842             }, true);
41843         
41844         this.titleEl.enableDisplayMode();
41845         /** This region's title text element 
41846         * @type HTMLElement */
41847         this.titleTextEl = this.titleEl.dom.firstChild;
41848         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41849         /*
41850         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41851         this.closeBtn.enableDisplayMode();
41852         this.closeBtn.on("click", this.closeClicked, this);
41853         this.closeBtn.hide();
41854     */
41855         this.createBody(this.config);
41856         if(this.config.hideWhenEmpty){
41857             this.hide();
41858             this.on("paneladded", this.validateVisibility, this);
41859             this.on("panelremoved", this.validateVisibility, this);
41860         }
41861         if(this.autoScroll){
41862             this.bodyEl.setStyle("overflow", "auto");
41863         }else{
41864             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41865         }
41866         //if(c.titlebar !== false){
41867             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41868                 this.titleEl.hide();
41869             }else{
41870                 this.titleEl.show();
41871                 if(this.config.title){
41872                     this.titleTextEl.innerHTML = this.config.title;
41873                 }
41874             }
41875         //}
41876         if(this.config.collapsed){
41877             this.collapse(true);
41878         }
41879         if(this.config.hidden){
41880             this.hide();
41881         }
41882         
41883         if (this.unrendered_panels && this.unrendered_panels.length) {
41884             for (var i =0;i< this.unrendered_panels.length; i++) {
41885                 this.add(this.unrendered_panels[i]);
41886             }
41887             this.unrendered_panels = null;
41888             
41889         }
41890         
41891     },
41892     
41893     applyConfig : function(c)
41894     {
41895         /*
41896          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41897             var dh = Roo.DomHelper;
41898             if(c.titlebar !== false){
41899                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41900                 this.collapseBtn.on("click", this.collapse, this);
41901                 this.collapseBtn.enableDisplayMode();
41902                 /*
41903                 if(c.showPin === true || this.showPin){
41904                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41905                     this.stickBtn.enableDisplayMode();
41906                     this.stickBtn.on("click", this.expand, this);
41907                     this.stickBtn.hide();
41908                 }
41909                 
41910             }
41911             */
41912             /** This region's collapsed element
41913             * @type Roo.Element */
41914             /*
41915              *
41916             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41917                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41918             ]}, true);
41919             
41920             if(c.floatable !== false){
41921                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41922                this.collapsedEl.on("click", this.collapseClick, this);
41923             }
41924
41925             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41926                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41927                    id: "message", unselectable: "on", style:{"float":"left"}});
41928                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41929              }
41930             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41931             this.expandBtn.on("click", this.expand, this);
41932             
41933         }
41934         
41935         if(this.collapseBtn){
41936             this.collapseBtn.setVisible(c.collapsible == true);
41937         }
41938         
41939         this.cmargins = c.cmargins || this.cmargins ||
41940                          (this.position == "west" || this.position == "east" ?
41941                              {top: 0, left: 2, right:2, bottom: 0} :
41942                              {top: 2, left: 0, right:0, bottom: 2});
41943         */
41944         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41945         
41946         
41947         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41948         
41949         this.autoScroll = c.autoScroll || false;
41950         
41951         
41952        
41953         
41954         this.duration = c.duration || .30;
41955         this.slideDuration = c.slideDuration || .45;
41956         this.config = c;
41957        
41958     },
41959     /**
41960      * Returns true if this region is currently visible.
41961      * @return {Boolean}
41962      */
41963     isVisible : function(){
41964         return this.visible;
41965     },
41966
41967     /**
41968      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41969      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41970      */
41971     //setCollapsedTitle : function(title){
41972     //    title = title || "&#160;";
41973      //   if(this.collapsedTitleTextEl){
41974       //      this.collapsedTitleTextEl.innerHTML = title;
41975        // }
41976     //},
41977
41978     getBox : function(){
41979         var b;
41980       //  if(!this.collapsed){
41981             b = this.el.getBox(false, true);
41982        // }else{
41983           //  b = this.collapsedEl.getBox(false, true);
41984         //}
41985         return b;
41986     },
41987
41988     getMargins : function(){
41989         return this.margins;
41990         //return this.collapsed ? this.cmargins : this.margins;
41991     },
41992 /*
41993     highlight : function(){
41994         this.el.addClass("x-layout-panel-dragover");
41995     },
41996
41997     unhighlight : function(){
41998         this.el.removeClass("x-layout-panel-dragover");
41999     },
42000 */
42001     updateBox : function(box)
42002     {
42003         if (!this.bodyEl) {
42004             return; // not rendered yet..
42005         }
42006         
42007         this.box = box;
42008         if(!this.collapsed){
42009             this.el.dom.style.left = box.x + "px";
42010             this.el.dom.style.top = box.y + "px";
42011             this.updateBody(box.width, box.height);
42012         }else{
42013             this.collapsedEl.dom.style.left = box.x + "px";
42014             this.collapsedEl.dom.style.top = box.y + "px";
42015             this.collapsedEl.setSize(box.width, box.height);
42016         }
42017         if(this.tabs){
42018             this.tabs.autoSizeTabs();
42019         }
42020     },
42021
42022     updateBody : function(w, h)
42023     {
42024         if(w !== null){
42025             this.el.setWidth(w);
42026             w -= this.el.getBorderWidth("rl");
42027             if(this.config.adjustments){
42028                 w += this.config.adjustments[0];
42029             }
42030         }
42031         if(h !== null && h > 0){
42032             this.el.setHeight(h);
42033             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42034             h -= this.el.getBorderWidth("tb");
42035             if(this.config.adjustments){
42036                 h += this.config.adjustments[1];
42037             }
42038             this.bodyEl.setHeight(h);
42039             if(this.tabs){
42040                 h = this.tabs.syncHeight(h);
42041             }
42042         }
42043         if(this.panelSize){
42044             w = w !== null ? w : this.panelSize.width;
42045             h = h !== null ? h : this.panelSize.height;
42046         }
42047         if(this.activePanel){
42048             var el = this.activePanel.getEl();
42049             w = w !== null ? w : el.getWidth();
42050             h = h !== null ? h : el.getHeight();
42051             this.panelSize = {width: w, height: h};
42052             this.activePanel.setSize(w, h);
42053         }
42054         if(Roo.isIE && this.tabs){
42055             this.tabs.el.repaint();
42056         }
42057     },
42058
42059     /**
42060      * Returns the container element for this region.
42061      * @return {Roo.Element}
42062      */
42063     getEl : function(){
42064         return this.el;
42065     },
42066
42067     /**
42068      * Hides this region.
42069      */
42070     hide : function(){
42071         //if(!this.collapsed){
42072             this.el.dom.style.left = "-2000px";
42073             this.el.hide();
42074         //}else{
42075          //   this.collapsedEl.dom.style.left = "-2000px";
42076          //   this.collapsedEl.hide();
42077        // }
42078         this.visible = false;
42079         this.fireEvent("visibilitychange", this, false);
42080     },
42081
42082     /**
42083      * Shows this region if it was previously hidden.
42084      */
42085     show : function(){
42086         //if(!this.collapsed){
42087             this.el.show();
42088         //}else{
42089         //    this.collapsedEl.show();
42090        // }
42091         this.visible = true;
42092         this.fireEvent("visibilitychange", this, true);
42093     },
42094 /*
42095     closeClicked : function(){
42096         if(this.activePanel){
42097             this.remove(this.activePanel);
42098         }
42099     },
42100
42101     collapseClick : function(e){
42102         if(this.isSlid){
42103            e.stopPropagation();
42104            this.slideIn();
42105         }else{
42106            e.stopPropagation();
42107            this.slideOut();
42108         }
42109     },
42110 */
42111     /**
42112      * Collapses this region.
42113      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42114      */
42115     /*
42116     collapse : function(skipAnim, skipCheck = false){
42117         if(this.collapsed) {
42118             return;
42119         }
42120         
42121         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42122             
42123             this.collapsed = true;
42124             if(this.split){
42125                 this.split.el.hide();
42126             }
42127             if(this.config.animate && skipAnim !== true){
42128                 this.fireEvent("invalidated", this);
42129                 this.animateCollapse();
42130             }else{
42131                 this.el.setLocation(-20000,-20000);
42132                 this.el.hide();
42133                 this.collapsedEl.show();
42134                 this.fireEvent("collapsed", this);
42135                 this.fireEvent("invalidated", this);
42136             }
42137         }
42138         
42139     },
42140 */
42141     animateCollapse : function(){
42142         // overridden
42143     },
42144
42145     /**
42146      * Expands this region if it was previously collapsed.
42147      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42148      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42149      */
42150     /*
42151     expand : function(e, skipAnim){
42152         if(e) {
42153             e.stopPropagation();
42154         }
42155         if(!this.collapsed || this.el.hasActiveFx()) {
42156             return;
42157         }
42158         if(this.isSlid){
42159             this.afterSlideIn();
42160             skipAnim = true;
42161         }
42162         this.collapsed = false;
42163         if(this.config.animate && skipAnim !== true){
42164             this.animateExpand();
42165         }else{
42166             this.el.show();
42167             if(this.split){
42168                 this.split.el.show();
42169             }
42170             this.collapsedEl.setLocation(-2000,-2000);
42171             this.collapsedEl.hide();
42172             this.fireEvent("invalidated", this);
42173             this.fireEvent("expanded", this);
42174         }
42175     },
42176 */
42177     animateExpand : function(){
42178         // overridden
42179     },
42180
42181     initTabs : function()
42182     {
42183         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42184         
42185         var ts = new Roo.bootstrap.panel.Tabs({
42186             el: this.bodyEl.dom,
42187             region : this,
42188             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42189             disableTooltips: this.config.disableTabTips,
42190             toolbar : this.config.toolbar
42191         });
42192         
42193         if(this.config.hideTabs){
42194             ts.stripWrap.setDisplayed(false);
42195         }
42196         this.tabs = ts;
42197         ts.resizeTabs = this.config.resizeTabs === true;
42198         ts.minTabWidth = this.config.minTabWidth || 40;
42199         ts.maxTabWidth = this.config.maxTabWidth || 250;
42200         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42201         ts.monitorResize = false;
42202         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42203         ts.bodyEl.addClass('roo-layout-tabs-body');
42204         this.panels.each(this.initPanelAsTab, this);
42205     },
42206
42207     initPanelAsTab : function(panel){
42208         var ti = this.tabs.addTab(
42209             panel.getEl().id,
42210             panel.getTitle(),
42211             null,
42212             this.config.closeOnTab && panel.isClosable(),
42213             panel.tpl
42214         );
42215         if(panel.tabTip !== undefined){
42216             ti.setTooltip(panel.tabTip);
42217         }
42218         ti.on("activate", function(){
42219               this.setActivePanel(panel);
42220         }, this);
42221         
42222         if(this.config.closeOnTab){
42223             ti.on("beforeclose", function(t, e){
42224                 e.cancel = true;
42225                 this.remove(panel);
42226             }, this);
42227         }
42228         
42229         panel.tabItem = ti;
42230         
42231         return ti;
42232     },
42233
42234     updatePanelTitle : function(panel, title)
42235     {
42236         if(this.activePanel == panel){
42237             this.updateTitle(title);
42238         }
42239         if(this.tabs){
42240             var ti = this.tabs.getTab(panel.getEl().id);
42241             ti.setText(title);
42242             if(panel.tabTip !== undefined){
42243                 ti.setTooltip(panel.tabTip);
42244             }
42245         }
42246     },
42247
42248     updateTitle : function(title){
42249         if(this.titleTextEl && !this.config.title){
42250             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42251         }
42252     },
42253
42254     setActivePanel : function(panel)
42255     {
42256         panel = this.getPanel(panel);
42257         if(this.activePanel && this.activePanel != panel){
42258             if(this.activePanel.setActiveState(false) === false){
42259                 return;
42260             }
42261         }
42262         this.activePanel = panel;
42263         panel.setActiveState(true);
42264         if(this.panelSize){
42265             panel.setSize(this.panelSize.width, this.panelSize.height);
42266         }
42267         if(this.closeBtn){
42268             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42269         }
42270         this.updateTitle(panel.getTitle());
42271         if(this.tabs){
42272             this.fireEvent("invalidated", this);
42273         }
42274         this.fireEvent("panelactivated", this, panel);
42275     },
42276
42277     /**
42278      * Shows the specified panel.
42279      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42280      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42281      */
42282     showPanel : function(panel)
42283     {
42284         panel = this.getPanel(panel);
42285         if(panel){
42286             if(this.tabs){
42287                 var tab = this.tabs.getTab(panel.getEl().id);
42288                 if(tab.isHidden()){
42289                     this.tabs.unhideTab(tab.id);
42290                 }
42291                 tab.activate();
42292             }else{
42293                 this.setActivePanel(panel);
42294             }
42295         }
42296         return panel;
42297     },
42298
42299     /**
42300      * Get the active panel for this region.
42301      * @return {Roo.ContentPanel} The active panel or null
42302      */
42303     getActivePanel : function(){
42304         return this.activePanel;
42305     },
42306
42307     validateVisibility : function(){
42308         if(this.panels.getCount() < 1){
42309             this.updateTitle("&#160;");
42310             this.closeBtn.hide();
42311             this.hide();
42312         }else{
42313             if(!this.isVisible()){
42314                 this.show();
42315             }
42316         }
42317     },
42318
42319     /**
42320      * Adds the passed ContentPanel(s) to this region.
42321      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42322      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42323      */
42324     add : function(panel)
42325     {
42326         if(arguments.length > 1){
42327             for(var i = 0, len = arguments.length; i < len; i++) {
42328                 this.add(arguments[i]);
42329             }
42330             return null;
42331         }
42332         
42333         // if we have not been rendered yet, then we can not really do much of this..
42334         if (!this.bodyEl) {
42335             this.unrendered_panels.push(panel);
42336             return panel;
42337         }
42338         
42339         
42340         
42341         
42342         if(this.hasPanel(panel)){
42343             this.showPanel(panel);
42344             return panel;
42345         }
42346         panel.setRegion(this);
42347         this.panels.add(panel);
42348        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42349             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42350             // and hide them... ???
42351             this.bodyEl.dom.appendChild(panel.getEl().dom);
42352             if(panel.background !== true){
42353                 this.setActivePanel(panel);
42354             }
42355             this.fireEvent("paneladded", this, panel);
42356             return panel;
42357         }
42358         */
42359         if(!this.tabs){
42360             this.initTabs();
42361         }else{
42362             this.initPanelAsTab(panel);
42363         }
42364         
42365         
42366         if(panel.background !== true){
42367             this.tabs.activate(panel.getEl().id);
42368         }
42369         this.fireEvent("paneladded", this, panel);
42370         return panel;
42371     },
42372
42373     /**
42374      * Hides the tab for the specified panel.
42375      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42376      */
42377     hidePanel : function(panel){
42378         if(this.tabs && (panel = this.getPanel(panel))){
42379             this.tabs.hideTab(panel.getEl().id);
42380         }
42381     },
42382
42383     /**
42384      * Unhides the tab for a previously hidden panel.
42385      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42386      */
42387     unhidePanel : function(panel){
42388         if(this.tabs && (panel = this.getPanel(panel))){
42389             this.tabs.unhideTab(panel.getEl().id);
42390         }
42391     },
42392
42393     clearPanels : function(){
42394         while(this.panels.getCount() > 0){
42395              this.remove(this.panels.first());
42396         }
42397     },
42398
42399     /**
42400      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42401      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42402      * @param {Boolean} preservePanel Overrides the config preservePanel option
42403      * @return {Roo.ContentPanel} The panel that was removed
42404      */
42405     remove : function(panel, preservePanel)
42406     {
42407         panel = this.getPanel(panel);
42408         if(!panel){
42409             return null;
42410         }
42411         var e = {};
42412         this.fireEvent("beforeremove", this, panel, e);
42413         if(e.cancel === true){
42414             return null;
42415         }
42416         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42417         var panelId = panel.getId();
42418         this.panels.removeKey(panelId);
42419         if(preservePanel){
42420             document.body.appendChild(panel.getEl().dom);
42421         }
42422         if(this.tabs){
42423             this.tabs.removeTab(panel.getEl().id);
42424         }else if (!preservePanel){
42425             this.bodyEl.dom.removeChild(panel.getEl().dom);
42426         }
42427         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42428             var p = this.panels.first();
42429             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42430             tempEl.appendChild(p.getEl().dom);
42431             this.bodyEl.update("");
42432             this.bodyEl.dom.appendChild(p.getEl().dom);
42433             tempEl = null;
42434             this.updateTitle(p.getTitle());
42435             this.tabs = null;
42436             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42437             this.setActivePanel(p);
42438         }
42439         panel.setRegion(null);
42440         if(this.activePanel == panel){
42441             this.activePanel = null;
42442         }
42443         if(this.config.autoDestroy !== false && preservePanel !== true){
42444             try{panel.destroy();}catch(e){}
42445         }
42446         this.fireEvent("panelremoved", this, panel);
42447         return panel;
42448     },
42449
42450     /**
42451      * Returns the TabPanel component used by this region
42452      * @return {Roo.TabPanel}
42453      */
42454     getTabs : function(){
42455         return this.tabs;
42456     },
42457
42458     createTool : function(parentEl, className){
42459         var btn = Roo.DomHelper.append(parentEl, {
42460             tag: "div",
42461             cls: "x-layout-tools-button",
42462             children: [ {
42463                 tag: "div",
42464                 cls: "roo-layout-tools-button-inner " + className,
42465                 html: "&#160;"
42466             }]
42467         }, true);
42468         btn.addClassOnOver("roo-layout-tools-button-over");
42469         return btn;
42470     }
42471 });/*
42472  * Based on:
42473  * Ext JS Library 1.1.1
42474  * Copyright(c) 2006-2007, Ext JS, LLC.
42475  *
42476  * Originally Released Under LGPL - original licence link has changed is not relivant.
42477  *
42478  * Fork - LGPL
42479  * <script type="text/javascript">
42480  */
42481  
42482
42483
42484 /**
42485  * @class Roo.SplitLayoutRegion
42486  * @extends Roo.LayoutRegion
42487  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42488  */
42489 Roo.bootstrap.layout.Split = function(config){
42490     this.cursor = config.cursor;
42491     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42492 };
42493
42494 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42495 {
42496     splitTip : "Drag to resize.",
42497     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42498     useSplitTips : false,
42499
42500     applyConfig : function(config){
42501         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42502     },
42503     
42504     onRender : function(ctr,pos) {
42505         
42506         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42507         if(!this.config.split){
42508             return;
42509         }
42510         if(!this.split){
42511             
42512             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42513                             tag: "div",
42514                             id: this.el.id + "-split",
42515                             cls: "roo-layout-split roo-layout-split-"+this.position,
42516                             html: "&#160;"
42517             });
42518             /** The SplitBar for this region 
42519             * @type Roo.SplitBar */
42520             // does not exist yet...
42521             Roo.log([this.position, this.orientation]);
42522             
42523             this.split = new Roo.bootstrap.SplitBar({
42524                 dragElement : splitEl,
42525                 resizingElement: this.el,
42526                 orientation : this.orientation
42527             });
42528             
42529             this.split.on("moved", this.onSplitMove, this);
42530             this.split.useShim = this.config.useShim === true;
42531             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42532             if(this.useSplitTips){
42533                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42534             }
42535             //if(config.collapsible){
42536             //    this.split.el.on("dblclick", this.collapse,  this);
42537             //}
42538         }
42539         if(typeof this.config.minSize != "undefined"){
42540             this.split.minSize = this.config.minSize;
42541         }
42542         if(typeof this.config.maxSize != "undefined"){
42543             this.split.maxSize = this.config.maxSize;
42544         }
42545         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42546             this.hideSplitter();
42547         }
42548         
42549     },
42550
42551     getHMaxSize : function(){
42552          var cmax = this.config.maxSize || 10000;
42553          var center = this.mgr.getRegion("center");
42554          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42555     },
42556
42557     getVMaxSize : function(){
42558          var cmax = this.config.maxSize || 10000;
42559          var center = this.mgr.getRegion("center");
42560          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42561     },
42562
42563     onSplitMove : function(split, newSize){
42564         this.fireEvent("resized", this, newSize);
42565     },
42566     
42567     /** 
42568      * Returns the {@link Roo.SplitBar} for this region.
42569      * @return {Roo.SplitBar}
42570      */
42571     getSplitBar : function(){
42572         return this.split;
42573     },
42574     
42575     hide : function(){
42576         this.hideSplitter();
42577         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42578     },
42579
42580     hideSplitter : function(){
42581         if(this.split){
42582             this.split.el.setLocation(-2000,-2000);
42583             this.split.el.hide();
42584         }
42585     },
42586
42587     show : function(){
42588         if(this.split){
42589             this.split.el.show();
42590         }
42591         Roo.bootstrap.layout.Split.superclass.show.call(this);
42592     },
42593     
42594     beforeSlide: function(){
42595         if(Roo.isGecko){// firefox overflow auto bug workaround
42596             this.bodyEl.clip();
42597             if(this.tabs) {
42598                 this.tabs.bodyEl.clip();
42599             }
42600             if(this.activePanel){
42601                 this.activePanel.getEl().clip();
42602                 
42603                 if(this.activePanel.beforeSlide){
42604                     this.activePanel.beforeSlide();
42605                 }
42606             }
42607         }
42608     },
42609     
42610     afterSlide : function(){
42611         if(Roo.isGecko){// firefox overflow auto bug workaround
42612             this.bodyEl.unclip();
42613             if(this.tabs) {
42614                 this.tabs.bodyEl.unclip();
42615             }
42616             if(this.activePanel){
42617                 this.activePanel.getEl().unclip();
42618                 if(this.activePanel.afterSlide){
42619                     this.activePanel.afterSlide();
42620                 }
42621             }
42622         }
42623     },
42624
42625     initAutoHide : function(){
42626         if(this.autoHide !== false){
42627             if(!this.autoHideHd){
42628                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42629                 this.autoHideHd = {
42630                     "mouseout": function(e){
42631                         if(!e.within(this.el, true)){
42632                             st.delay(500);
42633                         }
42634                     },
42635                     "mouseover" : function(e){
42636                         st.cancel();
42637                     },
42638                     scope : this
42639                 };
42640             }
42641             this.el.on(this.autoHideHd);
42642         }
42643     },
42644
42645     clearAutoHide : function(){
42646         if(this.autoHide !== false){
42647             this.el.un("mouseout", this.autoHideHd.mouseout);
42648             this.el.un("mouseover", this.autoHideHd.mouseover);
42649         }
42650     },
42651
42652     clearMonitor : function(){
42653         Roo.get(document).un("click", this.slideInIf, this);
42654     },
42655
42656     // these names are backwards but not changed for compat
42657     slideOut : function(){
42658         if(this.isSlid || this.el.hasActiveFx()){
42659             return;
42660         }
42661         this.isSlid = true;
42662         if(this.collapseBtn){
42663             this.collapseBtn.hide();
42664         }
42665         this.closeBtnState = this.closeBtn.getStyle('display');
42666         this.closeBtn.hide();
42667         if(this.stickBtn){
42668             this.stickBtn.show();
42669         }
42670         this.el.show();
42671         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42672         this.beforeSlide();
42673         this.el.setStyle("z-index", 10001);
42674         this.el.slideIn(this.getSlideAnchor(), {
42675             callback: function(){
42676                 this.afterSlide();
42677                 this.initAutoHide();
42678                 Roo.get(document).on("click", this.slideInIf, this);
42679                 this.fireEvent("slideshow", this);
42680             },
42681             scope: this,
42682             block: true
42683         });
42684     },
42685
42686     afterSlideIn : function(){
42687         this.clearAutoHide();
42688         this.isSlid = false;
42689         this.clearMonitor();
42690         this.el.setStyle("z-index", "");
42691         if(this.collapseBtn){
42692             this.collapseBtn.show();
42693         }
42694         this.closeBtn.setStyle('display', this.closeBtnState);
42695         if(this.stickBtn){
42696             this.stickBtn.hide();
42697         }
42698         this.fireEvent("slidehide", this);
42699     },
42700
42701     slideIn : function(cb){
42702         if(!this.isSlid || this.el.hasActiveFx()){
42703             Roo.callback(cb);
42704             return;
42705         }
42706         this.isSlid = false;
42707         this.beforeSlide();
42708         this.el.slideOut(this.getSlideAnchor(), {
42709             callback: function(){
42710                 this.el.setLeftTop(-10000, -10000);
42711                 this.afterSlide();
42712                 this.afterSlideIn();
42713                 Roo.callback(cb);
42714             },
42715             scope: this,
42716             block: true
42717         });
42718     },
42719     
42720     slideInIf : function(e){
42721         if(!e.within(this.el)){
42722             this.slideIn();
42723         }
42724     },
42725
42726     animateCollapse : function(){
42727         this.beforeSlide();
42728         this.el.setStyle("z-index", 20000);
42729         var anchor = this.getSlideAnchor();
42730         this.el.slideOut(anchor, {
42731             callback : function(){
42732                 this.el.setStyle("z-index", "");
42733                 this.collapsedEl.slideIn(anchor, {duration:.3});
42734                 this.afterSlide();
42735                 this.el.setLocation(-10000,-10000);
42736                 this.el.hide();
42737                 this.fireEvent("collapsed", this);
42738             },
42739             scope: this,
42740             block: true
42741         });
42742     },
42743
42744     animateExpand : function(){
42745         this.beforeSlide();
42746         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42747         this.el.setStyle("z-index", 20000);
42748         this.collapsedEl.hide({
42749             duration:.1
42750         });
42751         this.el.slideIn(this.getSlideAnchor(), {
42752             callback : function(){
42753                 this.el.setStyle("z-index", "");
42754                 this.afterSlide();
42755                 if(this.split){
42756                     this.split.el.show();
42757                 }
42758                 this.fireEvent("invalidated", this);
42759                 this.fireEvent("expanded", this);
42760             },
42761             scope: this,
42762             block: true
42763         });
42764     },
42765
42766     anchors : {
42767         "west" : "left",
42768         "east" : "right",
42769         "north" : "top",
42770         "south" : "bottom"
42771     },
42772
42773     sanchors : {
42774         "west" : "l",
42775         "east" : "r",
42776         "north" : "t",
42777         "south" : "b"
42778     },
42779
42780     canchors : {
42781         "west" : "tl-tr",
42782         "east" : "tr-tl",
42783         "north" : "tl-bl",
42784         "south" : "bl-tl"
42785     },
42786
42787     getAnchor : function(){
42788         return this.anchors[this.position];
42789     },
42790
42791     getCollapseAnchor : function(){
42792         return this.canchors[this.position];
42793     },
42794
42795     getSlideAnchor : function(){
42796         return this.sanchors[this.position];
42797     },
42798
42799     getAlignAdj : function(){
42800         var cm = this.cmargins;
42801         switch(this.position){
42802             case "west":
42803                 return [0, 0];
42804             break;
42805             case "east":
42806                 return [0, 0];
42807             break;
42808             case "north":
42809                 return [0, 0];
42810             break;
42811             case "south":
42812                 return [0, 0];
42813             break;
42814         }
42815     },
42816
42817     getExpandAdj : function(){
42818         var c = this.collapsedEl, cm = this.cmargins;
42819         switch(this.position){
42820             case "west":
42821                 return [-(cm.right+c.getWidth()+cm.left), 0];
42822             break;
42823             case "east":
42824                 return [cm.right+c.getWidth()+cm.left, 0];
42825             break;
42826             case "north":
42827                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42828             break;
42829             case "south":
42830                 return [0, cm.top+cm.bottom+c.getHeight()];
42831             break;
42832         }
42833     }
42834 });/*
42835  * Based on:
42836  * Ext JS Library 1.1.1
42837  * Copyright(c) 2006-2007, Ext JS, LLC.
42838  *
42839  * Originally Released Under LGPL - original licence link has changed is not relivant.
42840  *
42841  * Fork - LGPL
42842  * <script type="text/javascript">
42843  */
42844 /*
42845  * These classes are private internal classes
42846  */
42847 Roo.bootstrap.layout.Center = function(config){
42848     config.region = "center";
42849     Roo.bootstrap.layout.Region.call(this, config);
42850     this.visible = true;
42851     this.minWidth = config.minWidth || 20;
42852     this.minHeight = config.minHeight || 20;
42853 };
42854
42855 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42856     hide : function(){
42857         // center panel can't be hidden
42858     },
42859     
42860     show : function(){
42861         // center panel can't be hidden
42862     },
42863     
42864     getMinWidth: function(){
42865         return this.minWidth;
42866     },
42867     
42868     getMinHeight: function(){
42869         return this.minHeight;
42870     }
42871 });
42872
42873
42874
42875
42876  
42877
42878
42879
42880
42881
42882
42883 Roo.bootstrap.layout.North = function(config)
42884 {
42885     config.region = 'north';
42886     config.cursor = 'n-resize';
42887     
42888     Roo.bootstrap.layout.Split.call(this, config);
42889     
42890     
42891     if(this.split){
42892         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42893         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42894         this.split.el.addClass("roo-layout-split-v");
42895     }
42896     //var size = config.initialSize || config.height;
42897     //if(this.el && typeof size != "undefined"){
42898     //    this.el.setHeight(size);
42899     //}
42900 };
42901 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42902 {
42903     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42904      
42905      
42906     onRender : function(ctr, pos)
42907     {
42908         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42909         var size = this.config.initialSize || this.config.height;
42910         if(this.el && typeof size != "undefined"){
42911             this.el.setHeight(size);
42912         }
42913     
42914     },
42915     
42916     getBox : function(){
42917         if(this.collapsed){
42918             return this.collapsedEl.getBox();
42919         }
42920         var box = this.el.getBox();
42921         if(this.split){
42922             box.height += this.split.el.getHeight();
42923         }
42924         return box;
42925     },
42926     
42927     updateBox : function(box){
42928         if(this.split && !this.collapsed){
42929             box.height -= this.split.el.getHeight();
42930             this.split.el.setLeft(box.x);
42931             this.split.el.setTop(box.y+box.height);
42932             this.split.el.setWidth(box.width);
42933         }
42934         if(this.collapsed){
42935             this.updateBody(box.width, null);
42936         }
42937         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42938     }
42939 });
42940
42941
42942
42943
42944
42945 Roo.bootstrap.layout.South = function(config){
42946     config.region = 'south';
42947     config.cursor = 's-resize';
42948     Roo.bootstrap.layout.Split.call(this, config);
42949     if(this.split){
42950         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42951         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42952         this.split.el.addClass("roo-layout-split-v");
42953     }
42954     
42955 };
42956
42957 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42958     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42959     
42960     onRender : function(ctr, pos)
42961     {
42962         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42963         var size = this.config.initialSize || this.config.height;
42964         if(this.el && typeof size != "undefined"){
42965             this.el.setHeight(size);
42966         }
42967     
42968     },
42969     
42970     getBox : function(){
42971         if(this.collapsed){
42972             return this.collapsedEl.getBox();
42973         }
42974         var box = this.el.getBox();
42975         if(this.split){
42976             var sh = this.split.el.getHeight();
42977             box.height += sh;
42978             box.y -= sh;
42979         }
42980         return box;
42981     },
42982     
42983     updateBox : function(box){
42984         if(this.split && !this.collapsed){
42985             var sh = this.split.el.getHeight();
42986             box.height -= sh;
42987             box.y += sh;
42988             this.split.el.setLeft(box.x);
42989             this.split.el.setTop(box.y-sh);
42990             this.split.el.setWidth(box.width);
42991         }
42992         if(this.collapsed){
42993             this.updateBody(box.width, null);
42994         }
42995         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42996     }
42997 });
42998
42999 Roo.bootstrap.layout.East = function(config){
43000     config.region = "east";
43001     config.cursor = "e-resize";
43002     Roo.bootstrap.layout.Split.call(this, config);
43003     if(this.split){
43004         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
43005         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43006         this.split.el.addClass("roo-layout-split-h");
43007     }
43008     
43009 };
43010 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43011     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43012     
43013     onRender : function(ctr, pos)
43014     {
43015         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43016         var size = this.config.initialSize || this.config.width;
43017         if(this.el && typeof size != "undefined"){
43018             this.el.setWidth(size);
43019         }
43020     
43021     },
43022     
43023     getBox : function(){
43024         if(this.collapsed){
43025             return this.collapsedEl.getBox();
43026         }
43027         var box = this.el.getBox();
43028         if(this.split){
43029             var sw = this.split.el.getWidth();
43030             box.width += sw;
43031             box.x -= sw;
43032         }
43033         return box;
43034     },
43035
43036     updateBox : function(box){
43037         if(this.split && !this.collapsed){
43038             var sw = this.split.el.getWidth();
43039             box.width -= sw;
43040             this.split.el.setLeft(box.x);
43041             this.split.el.setTop(box.y);
43042             this.split.el.setHeight(box.height);
43043             box.x += sw;
43044         }
43045         if(this.collapsed){
43046             this.updateBody(null, box.height);
43047         }
43048         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43049     }
43050 });
43051
43052 Roo.bootstrap.layout.West = function(config){
43053     config.region = "west";
43054     config.cursor = "w-resize";
43055     
43056     Roo.bootstrap.layout.Split.call(this, config);
43057     if(this.split){
43058         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43059         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43060         this.split.el.addClass("roo-layout-split-h");
43061     }
43062     
43063 };
43064 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43065     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43066     
43067     onRender: function(ctr, pos)
43068     {
43069         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43070         var size = this.config.initialSize || this.config.width;
43071         if(typeof size != "undefined"){
43072             this.el.setWidth(size);
43073         }
43074     },
43075     
43076     getBox : function(){
43077         if(this.collapsed){
43078             return this.collapsedEl.getBox();
43079         }
43080         var box = this.el.getBox();
43081         if (box.width == 0) {
43082             box.width = this.config.width; // kludge?
43083         }
43084         if(this.split){
43085             box.width += this.split.el.getWidth();
43086         }
43087         return box;
43088     },
43089     
43090     updateBox : function(box){
43091         if(this.split && !this.collapsed){
43092             var sw = this.split.el.getWidth();
43093             box.width -= sw;
43094             this.split.el.setLeft(box.x+box.width);
43095             this.split.el.setTop(box.y);
43096             this.split.el.setHeight(box.height);
43097         }
43098         if(this.collapsed){
43099             this.updateBody(null, box.height);
43100         }
43101         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43102     }
43103 });/*
43104  * Based on:
43105  * Ext JS Library 1.1.1
43106  * Copyright(c) 2006-2007, Ext JS, LLC.
43107  *
43108  * Originally Released Under LGPL - original licence link has changed is not relivant.
43109  *
43110  * Fork - LGPL
43111  * <script type="text/javascript">
43112  */
43113 /**
43114  * @class Roo.bootstrap.paenl.Content
43115  * @extends Roo.util.Observable
43116  * @children Roo.bootstrap.Component
43117  * @parent builder Roo.bootstrap.layout.Border
43118  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43119  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43120  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43121  * @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
43122  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43123  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43124  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43125  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43126  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43127  * @cfg {String} title          The title for this panel
43128  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43129  * @cfg {String} url            Calls {@link #setUrl} with this value
43130  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43131  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43132  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43133  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43134  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43135  * @cfg {Boolean} badges render the badges
43136  * @cfg {String} cls  extra classes to use  
43137  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43138  
43139  * @constructor
43140  * Create a new ContentPanel.
43141  * @param {String/Object} config A string to set only the title or a config object
43142  
43143  */
43144 Roo.bootstrap.panel.Content = function( config){
43145     
43146     this.tpl = config.tpl || false;
43147     
43148     var el = config.el;
43149     var content = config.content;
43150
43151     if(config.autoCreate){ // xtype is available if this is called from factory
43152         el = Roo.id();
43153     }
43154     this.el = Roo.get(el);
43155     if(!this.el && config && config.autoCreate){
43156         if(typeof config.autoCreate == "object"){
43157             if(!config.autoCreate.id){
43158                 config.autoCreate.id = config.id||el;
43159             }
43160             this.el = Roo.DomHelper.append(document.body,
43161                         config.autoCreate, true);
43162         }else{
43163             var elcfg =  {
43164                 tag: "div",
43165                 cls: (config.cls || '') +
43166                     (config.background ? ' bg-' + config.background : '') +
43167                     " roo-layout-inactive-content",
43168                 id: config.id||el
43169             };
43170             if (config.iframe) {
43171                 elcfg.cn = [
43172                     {
43173                         tag : 'iframe',
43174                         style : 'border: 0px',
43175                         src : 'about:blank'
43176                     }
43177                 ];
43178             }
43179               
43180             if (config.html) {
43181                 elcfg.html = config.html;
43182                 
43183             }
43184                         
43185             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43186             if (config.iframe) {
43187                 this.iframeEl = this.el.select('iframe',true).first();
43188             }
43189             
43190         }
43191     } 
43192     this.closable = false;
43193     this.loaded = false;
43194     this.active = false;
43195    
43196       
43197     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43198         
43199         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43200         
43201         this.wrapEl = this.el; //this.el.wrap();
43202         var ti = [];
43203         if (config.toolbar.items) {
43204             ti = config.toolbar.items ;
43205             delete config.toolbar.items ;
43206         }
43207         
43208         var nitems = [];
43209         this.toolbar.render(this.wrapEl, 'before');
43210         for(var i =0;i < ti.length;i++) {
43211           //  Roo.log(['add child', items[i]]);
43212             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43213         }
43214         this.toolbar.items = nitems;
43215         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43216         delete config.toolbar;
43217         
43218     }
43219     /*
43220     // xtype created footer. - not sure if will work as we normally have to render first..
43221     if (this.footer && !this.footer.el && this.footer.xtype) {
43222         if (!this.wrapEl) {
43223             this.wrapEl = this.el.wrap();
43224         }
43225     
43226         this.footer.container = this.wrapEl.createChild();
43227          
43228         this.footer = Roo.factory(this.footer, Roo);
43229         
43230     }
43231     */
43232     
43233      if(typeof config == "string"){
43234         this.title = config;
43235     }else{
43236         Roo.apply(this, config);
43237     }
43238     
43239     if(this.resizeEl){
43240         this.resizeEl = Roo.get(this.resizeEl, true);
43241     }else{
43242         this.resizeEl = this.el;
43243     }
43244     // handle view.xtype
43245     
43246  
43247     
43248     
43249     this.addEvents({
43250         /**
43251          * @event activate
43252          * Fires when this panel is activated. 
43253          * @param {Roo.ContentPanel} this
43254          */
43255         "activate" : true,
43256         /**
43257          * @event deactivate
43258          * Fires when this panel is activated. 
43259          * @param {Roo.ContentPanel} this
43260          */
43261         "deactivate" : true,
43262
43263         /**
43264          * @event resize
43265          * Fires when this panel is resized if fitToFrame is true.
43266          * @param {Roo.ContentPanel} this
43267          * @param {Number} width The width after any component adjustments
43268          * @param {Number} height The height after any component adjustments
43269          */
43270         "resize" : true,
43271         
43272          /**
43273          * @event render
43274          * Fires when this tab is created
43275          * @param {Roo.ContentPanel} this
43276          */
43277         "render" : true,
43278         
43279           /**
43280          * @event scroll
43281          * Fires when this content is scrolled
43282          * @param {Roo.ContentPanel} this
43283          * @param {Event} scrollEvent
43284          */
43285         "scroll" : true
43286         
43287         
43288         
43289     });
43290     
43291
43292     
43293     
43294     if(this.autoScroll && !this.iframe){
43295         this.resizeEl.setStyle("overflow", "auto");
43296         this.resizeEl.on('scroll', this.onScroll, this);
43297     } else {
43298         // fix randome scrolling
43299         //this.el.on('scroll', function() {
43300         //    Roo.log('fix random scolling');
43301         //    this.scrollTo('top',0); 
43302         //});
43303     }
43304     content = content || this.content;
43305     if(content){
43306         this.setContent(content);
43307     }
43308     if(config && config.url){
43309         this.setUrl(this.url, this.params, this.loadOnce);
43310     }
43311     
43312     
43313     
43314     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43315     
43316     if (this.view && typeof(this.view.xtype) != 'undefined') {
43317         this.view.el = this.el.appendChild(document.createElement("div"));
43318         this.view = Roo.factory(this.view); 
43319         this.view.render  &&  this.view.render(false, '');  
43320     }
43321     
43322     
43323     this.fireEvent('render', this);
43324 };
43325
43326 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43327     
43328     cls : '',
43329     background : '',
43330     
43331     tabTip : '',
43332     
43333     iframe : false,
43334     iframeEl : false,
43335     
43336     /* Resize Element - use this to work out scroll etc. */
43337     resizeEl : false,
43338     
43339     setRegion : function(region){
43340         this.region = region;
43341         this.setActiveClass(region && !this.background);
43342     },
43343     
43344     
43345     setActiveClass: function(state)
43346     {
43347         if(state){
43348            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43349            this.el.setStyle('position','relative');
43350         }else{
43351            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43352            this.el.setStyle('position', 'absolute');
43353         } 
43354     },
43355     
43356     /**
43357      * Returns the toolbar for this Panel if one was configured. 
43358      * @return {Roo.Toolbar} 
43359      */
43360     getToolbar : function(){
43361         return this.toolbar;
43362     },
43363     
43364     setActiveState : function(active)
43365     {
43366         this.active = active;
43367         this.setActiveClass(active);
43368         if(!active){
43369             if(this.fireEvent("deactivate", this) === false){
43370                 return false;
43371             }
43372             return true;
43373         }
43374         this.fireEvent("activate", this);
43375         return true;
43376     },
43377     /**
43378      * Updates this panel's element (not for iframe)
43379      * @param {String} content The new content
43380      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43381     */
43382     setContent : function(content, loadScripts){
43383         if (this.iframe) {
43384             return;
43385         }
43386         
43387         this.el.update(content, loadScripts);
43388     },
43389
43390     ignoreResize : function(w, h)
43391     {
43392         //return false; // always resize?
43393         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43394             return true;
43395         }else{
43396             this.lastSize = {width: w, height: h};
43397             return false;
43398         }
43399     },
43400     /**
43401      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43402      * @return {Roo.UpdateManager} The UpdateManager
43403      */
43404     getUpdateManager : function(){
43405         if (this.iframe) {
43406             return false;
43407         }
43408         return this.el.getUpdateManager();
43409     },
43410      /**
43411      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43412      * Does not work with IFRAME contents
43413      * @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:
43414 <pre><code>
43415 panel.load({
43416     url: "your-url.php",
43417     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43418     callback: yourFunction,
43419     scope: yourObject, //(optional scope)
43420     discardUrl: false,
43421     nocache: false,
43422     text: "Loading...",
43423     timeout: 30,
43424     scripts: false
43425 });
43426 </code></pre>
43427      
43428      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43429      * 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.
43430      * @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}
43431      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43432      * @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.
43433      * @return {Roo.ContentPanel} this
43434      */
43435     load : function(){
43436         
43437         if (this.iframe) {
43438             return this;
43439         }
43440         
43441         var um = this.el.getUpdateManager();
43442         um.update.apply(um, arguments);
43443         return this;
43444     },
43445
43446
43447     /**
43448      * 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.
43449      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43450      * @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)
43451      * @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)
43452      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43453      */
43454     setUrl : function(url, params, loadOnce){
43455         if (this.iframe) {
43456             this.iframeEl.dom.src = url;
43457             return false;
43458         }
43459         
43460         if(this.refreshDelegate){
43461             this.removeListener("activate", this.refreshDelegate);
43462         }
43463         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43464         this.on("activate", this.refreshDelegate);
43465         return this.el.getUpdateManager();
43466     },
43467     
43468     _handleRefresh : function(url, params, loadOnce){
43469         if(!loadOnce || !this.loaded){
43470             var updater = this.el.getUpdateManager();
43471             updater.update(url, params, this._setLoaded.createDelegate(this));
43472         }
43473     },
43474     
43475     _setLoaded : function(){
43476         this.loaded = true;
43477     }, 
43478     
43479     /**
43480      * Returns this panel's id
43481      * @return {String} 
43482      */
43483     getId : function(){
43484         return this.el.id;
43485     },
43486     
43487     /** 
43488      * Returns this panel's element - used by regiosn to add.
43489      * @return {Roo.Element} 
43490      */
43491     getEl : function(){
43492         return this.wrapEl || this.el;
43493     },
43494     
43495    
43496     
43497     adjustForComponents : function(width, height)
43498     {
43499         //Roo.log('adjustForComponents ');
43500         if(this.resizeEl != this.el){
43501             width -= this.el.getFrameWidth('lr');
43502             height -= this.el.getFrameWidth('tb');
43503         }
43504         if(this.toolbar){
43505             var te = this.toolbar.getEl();
43506             te.setWidth(width);
43507             height -= te.getHeight();
43508         }
43509         if(this.footer){
43510             var te = this.footer.getEl();
43511             te.setWidth(width);
43512             height -= te.getHeight();
43513         }
43514         
43515         
43516         if(this.adjustments){
43517             width += this.adjustments[0];
43518             height += this.adjustments[1];
43519         }
43520         return {"width": width, "height": height};
43521     },
43522     
43523     setSize : function(width, height){
43524         if(this.fitToFrame && !this.ignoreResize(width, height)){
43525             if(this.fitContainer && this.resizeEl != this.el){
43526                 this.el.setSize(width, height);
43527             }
43528             var size = this.adjustForComponents(width, height);
43529             if (this.iframe) {
43530                 this.iframeEl.setSize(width,height);
43531             }
43532             
43533             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43534             this.fireEvent('resize', this, size.width, size.height);
43535             
43536             
43537         }
43538     },
43539     
43540     /**
43541      * Returns this panel's title
43542      * @return {String} 
43543      */
43544     getTitle : function(){
43545         
43546         if (typeof(this.title) != 'object') {
43547             return this.title;
43548         }
43549         
43550         var t = '';
43551         for (var k in this.title) {
43552             if (!this.title.hasOwnProperty(k)) {
43553                 continue;
43554             }
43555             
43556             if (k.indexOf('-') >= 0) {
43557                 var s = k.split('-');
43558                 for (var i = 0; i<s.length; i++) {
43559                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43560                 }
43561             } else {
43562                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43563             }
43564         }
43565         return t;
43566     },
43567     
43568     /**
43569      * Set this panel's title
43570      * @param {String} title
43571      */
43572     setTitle : function(title){
43573         this.title = title;
43574         if(this.region){
43575             this.region.updatePanelTitle(this, title);
43576         }
43577     },
43578     
43579     /**
43580      * Returns true is this panel was configured to be closable
43581      * @return {Boolean} 
43582      */
43583     isClosable : function(){
43584         return this.closable;
43585     },
43586     
43587     beforeSlide : function(){
43588         this.el.clip();
43589         this.resizeEl.clip();
43590     },
43591     
43592     afterSlide : function(){
43593         this.el.unclip();
43594         this.resizeEl.unclip();
43595     },
43596     
43597     /**
43598      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43599      *   Will fail silently if the {@link #setUrl} method has not been called.
43600      *   This does not activate the panel, just updates its content.
43601      */
43602     refresh : function(){
43603         if(this.refreshDelegate){
43604            this.loaded = false;
43605            this.refreshDelegate();
43606         }
43607     },
43608     
43609     /**
43610      * Destroys this panel
43611      */
43612     destroy : function(){
43613         this.el.removeAllListeners();
43614         var tempEl = document.createElement("span");
43615         tempEl.appendChild(this.el.dom);
43616         tempEl.innerHTML = "";
43617         this.el.remove();
43618         this.el = null;
43619     },
43620     
43621     /**
43622      * form - if the content panel contains a form - this is a reference to it.
43623      * @type {Roo.form.Form}
43624      */
43625     form : false,
43626     /**
43627      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43628      *    This contains a reference to it.
43629      * @type {Roo.View}
43630      */
43631     view : false,
43632     
43633       /**
43634      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43635      * <pre><code>
43636
43637 layout.addxtype({
43638        xtype : 'Form',
43639        items: [ .... ]
43640    }
43641 );
43642
43643 </code></pre>
43644      * @param {Object} cfg Xtype definition of item to add.
43645      */
43646     
43647     
43648     getChildContainer: function () {
43649         return this.getEl();
43650     },
43651     
43652     
43653     onScroll : function(e)
43654     {
43655         this.fireEvent('scroll', this, e);
43656     }
43657     
43658     
43659     /*
43660         var  ret = new Roo.factory(cfg);
43661         return ret;
43662         
43663         
43664         // add form..
43665         if (cfg.xtype.match(/^Form$/)) {
43666             
43667             var el;
43668             //if (this.footer) {
43669             //    el = this.footer.container.insertSibling(false, 'before');
43670             //} else {
43671                 el = this.el.createChild();
43672             //}
43673
43674             this.form = new  Roo.form.Form(cfg);
43675             
43676             
43677             if ( this.form.allItems.length) {
43678                 this.form.render(el.dom);
43679             }
43680             return this.form;
43681         }
43682         // should only have one of theses..
43683         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43684             // views.. should not be just added - used named prop 'view''
43685             
43686             cfg.el = this.el.appendChild(document.createElement("div"));
43687             // factory?
43688             
43689             var ret = new Roo.factory(cfg);
43690              
43691              ret.render && ret.render(false, ''); // render blank..
43692             this.view = ret;
43693             return ret;
43694         }
43695         return false;
43696     }
43697     \*/
43698 });
43699  
43700 /**
43701  * @class Roo.bootstrap.panel.Grid
43702  * @extends Roo.bootstrap.panel.Content
43703  * @constructor
43704  * Create a new GridPanel.
43705  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43706  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43707  * @param {Object} config A the config object
43708   
43709  */
43710
43711
43712
43713 Roo.bootstrap.panel.Grid = function(config)
43714 {
43715     
43716       
43717     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43718         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43719
43720     config.el = this.wrapper;
43721     //this.el = this.wrapper;
43722     
43723       if (config.container) {
43724         // ctor'ed from a Border/panel.grid
43725         
43726         
43727         this.wrapper.setStyle("overflow", "hidden");
43728         this.wrapper.addClass('roo-grid-container');
43729
43730     }
43731     
43732     
43733     if(config.toolbar){
43734         var tool_el = this.wrapper.createChild();    
43735         this.toolbar = Roo.factory(config.toolbar);
43736         var ti = [];
43737         if (config.toolbar.items) {
43738             ti = config.toolbar.items ;
43739             delete config.toolbar.items ;
43740         }
43741         
43742         var nitems = [];
43743         this.toolbar.render(tool_el);
43744         for(var i =0;i < ti.length;i++) {
43745           //  Roo.log(['add child', items[i]]);
43746             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43747         }
43748         this.toolbar.items = nitems;
43749         
43750         delete config.toolbar;
43751     }
43752     
43753     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43754     config.grid.scrollBody = true;;
43755     config.grid.monitorWindowResize = false; // turn off autosizing
43756     config.grid.autoHeight = false;
43757     config.grid.autoWidth = false;
43758     
43759     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43760     
43761     if (config.background) {
43762         // render grid on panel activation (if panel background)
43763         this.on('activate', function(gp) {
43764             if (!gp.grid.rendered) {
43765                 gp.grid.render(this.wrapper);
43766                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43767             }
43768         });
43769             
43770     } else {
43771         this.grid.render(this.wrapper);
43772         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43773
43774     }
43775     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43776     // ??? needed ??? config.el = this.wrapper;
43777     
43778     
43779     
43780   
43781     // xtype created footer. - not sure if will work as we normally have to render first..
43782     if (this.footer && !this.footer.el && this.footer.xtype) {
43783         
43784         var ctr = this.grid.getView().getFooterPanel(true);
43785         this.footer.dataSource = this.grid.dataSource;
43786         this.footer = Roo.factory(this.footer, Roo);
43787         this.footer.render(ctr);
43788         
43789     }
43790     
43791     
43792     
43793     
43794      
43795 };
43796
43797 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43798 {
43799   
43800     getId : function(){
43801         return this.grid.id;
43802     },
43803     
43804     /**
43805      * Returns the grid for this panel
43806      * @return {Roo.bootstrap.Table} 
43807      */
43808     getGrid : function(){
43809         return this.grid;    
43810     },
43811     
43812     setSize : function(width, height)
43813     {
43814      
43815         //if(!this.ignoreResize(width, height)){
43816             var grid = this.grid;
43817             var size = this.adjustForComponents(width, height);
43818             // tfoot is not a footer?
43819           
43820             
43821             var gridel = grid.getGridEl();
43822             gridel.setSize(size.width, size.height);
43823             
43824             var tbd = grid.getGridEl().select('tbody', true).first();
43825             var thd = grid.getGridEl().select('thead',true).first();
43826             var tbf= grid.getGridEl().select('tfoot', true).first();
43827
43828             if (tbf) {
43829                 size.height -= tbf.getHeight();
43830             }
43831             if (thd) {
43832                 size.height -= thd.getHeight();
43833             }
43834             
43835             tbd.setSize(size.width, size.height );
43836             // this is for the account management tab -seems to work there.
43837             var thd = grid.getGridEl().select('thead',true).first();
43838             //if (tbd) {
43839             //    tbd.setSize(size.width, size.height - thd.getHeight());
43840             //}
43841              
43842             grid.autoSize();
43843         //}
43844    
43845     },
43846      
43847     
43848     
43849     beforeSlide : function(){
43850         this.grid.getView().scroller.clip();
43851     },
43852     
43853     afterSlide : function(){
43854         this.grid.getView().scroller.unclip();
43855     },
43856     
43857     destroy : function(){
43858         this.grid.destroy();
43859         delete this.grid;
43860         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43861     }
43862 });
43863
43864 /**
43865  * @class Roo.bootstrap.panel.Nest
43866  * @extends Roo.bootstrap.panel.Content
43867  * @constructor
43868  * Create a new Panel, that can contain a layout.Border.
43869  * 
43870  * 
43871  * @param {String/Object} config A string to set only the title or a config object
43872  */
43873 Roo.bootstrap.panel.Nest = function(config)
43874 {
43875     // construct with only one argument..
43876     /* FIXME - implement nicer consturctors
43877     if (layout.layout) {
43878         config = layout;
43879         layout = config.layout;
43880         delete config.layout;
43881     }
43882     if (layout.xtype && !layout.getEl) {
43883         // then layout needs constructing..
43884         layout = Roo.factory(layout, Roo);
43885     }
43886     */
43887     
43888     config.el =  config.layout.getEl();
43889     
43890     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43891     
43892     config.layout.monitorWindowResize = false; // turn off autosizing
43893     this.layout = config.layout;
43894     this.layout.getEl().addClass("roo-layout-nested-layout");
43895     this.layout.parent = this;
43896     
43897     
43898     
43899     
43900 };
43901
43902 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43903     /**
43904     * @cfg {Roo.BorderLayout} layout The layout for this panel
43905     */
43906     layout : false,
43907
43908     setSize : function(width, height){
43909         if(!this.ignoreResize(width, height)){
43910             var size = this.adjustForComponents(width, height);
43911             var el = this.layout.getEl();
43912             if (size.height < 1) {
43913                 el.setWidth(size.width);   
43914             } else {
43915                 el.setSize(size.width, size.height);
43916             }
43917             var touch = el.dom.offsetWidth;
43918             this.layout.layout();
43919             // ie requires a double layout on the first pass
43920             if(Roo.isIE && !this.initialized){
43921                 this.initialized = true;
43922                 this.layout.layout();
43923             }
43924         }
43925     },
43926     
43927     // activate all subpanels if not currently active..
43928     
43929     setActiveState : function(active){
43930         this.active = active;
43931         this.setActiveClass(active);
43932         
43933         if(!active){
43934             this.fireEvent("deactivate", this);
43935             return;
43936         }
43937         
43938         this.fireEvent("activate", this);
43939         // not sure if this should happen before or after..
43940         if (!this.layout) {
43941             return; // should not happen..
43942         }
43943         var reg = false;
43944         for (var r in this.layout.regions) {
43945             reg = this.layout.getRegion(r);
43946             if (reg.getActivePanel()) {
43947                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43948                 reg.setActivePanel(reg.getActivePanel());
43949                 continue;
43950             }
43951             if (!reg.panels.length) {
43952                 continue;
43953             }
43954             reg.showPanel(reg.getPanel(0));
43955         }
43956         
43957         
43958         
43959         
43960     },
43961     
43962     /**
43963      * Returns the nested BorderLayout for this panel
43964      * @return {Roo.BorderLayout} 
43965      */
43966     getLayout : function(){
43967         return this.layout;
43968     },
43969     
43970      /**
43971      * Adds a xtype elements to the layout of the nested panel
43972      * <pre><code>
43973
43974 panel.addxtype({
43975        xtype : 'ContentPanel',
43976        region: 'west',
43977        items: [ .... ]
43978    }
43979 );
43980
43981 panel.addxtype({
43982         xtype : 'NestedLayoutPanel',
43983         region: 'west',
43984         layout: {
43985            center: { },
43986            west: { }   
43987         },
43988         items : [ ... list of content panels or nested layout panels.. ]
43989    }
43990 );
43991 </code></pre>
43992      * @param {Object} cfg Xtype definition of item to add.
43993      */
43994     addxtype : function(cfg) {
43995         return this.layout.addxtype(cfg);
43996     
43997     }
43998 });/*
43999  * Based on:
44000  * Ext JS Library 1.1.1
44001  * Copyright(c) 2006-2007, Ext JS, LLC.
44002  *
44003  * Originally Released Under LGPL - original licence link has changed is not relivant.
44004  *
44005  * Fork - LGPL
44006  * <script type="text/javascript">
44007  */
44008 /**
44009  * @class Roo.TabPanel
44010  * @extends Roo.util.Observable
44011  * A lightweight tab container.
44012  * <br><br>
44013  * Usage:
44014  * <pre><code>
44015 // basic tabs 1, built from existing content
44016 var tabs = new Roo.TabPanel("tabs1");
44017 tabs.addTab("script", "View Script");
44018 tabs.addTab("markup", "View Markup");
44019 tabs.activate("script");
44020
44021 // more advanced tabs, built from javascript
44022 var jtabs = new Roo.TabPanel("jtabs");
44023 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44024
44025 // set up the UpdateManager
44026 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44027 var updater = tab2.getUpdateManager();
44028 updater.setDefaultUrl("ajax1.htm");
44029 tab2.on('activate', updater.refresh, updater, true);
44030
44031 // Use setUrl for Ajax loading
44032 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44033 tab3.setUrl("ajax2.htm", null, true);
44034
44035 // Disabled tab
44036 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44037 tab4.disable();
44038
44039 jtabs.activate("jtabs-1");
44040  * </code></pre>
44041  * @constructor
44042  * Create a new TabPanel.
44043  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44044  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44045  */
44046 Roo.bootstrap.panel.Tabs = function(config){
44047     /**
44048     * The container element for this TabPanel.
44049     * @type Roo.Element
44050     */
44051     this.el = Roo.get(config.el);
44052     delete config.el;
44053     if(config){
44054         if(typeof config == "boolean"){
44055             this.tabPosition = config ? "bottom" : "top";
44056         }else{
44057             Roo.apply(this, config);
44058         }
44059     }
44060     
44061     if(this.tabPosition == "bottom"){
44062         // if tabs are at the bottom = create the body first.
44063         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44064         this.el.addClass("roo-tabs-bottom");
44065     }
44066     // next create the tabs holders
44067     
44068     if (this.tabPosition == "west"){
44069         
44070         var reg = this.region; // fake it..
44071         while (reg) {
44072             if (!reg.mgr.parent) {
44073                 break;
44074             }
44075             reg = reg.mgr.parent.region;
44076         }
44077         Roo.log("got nest?");
44078         Roo.log(reg);
44079         if (reg.mgr.getRegion('west')) {
44080             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44081             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44082             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44083             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44084             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44085         
44086             
44087         }
44088         
44089         
44090     } else {
44091      
44092         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44093         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44094         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44095         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44096     }
44097     
44098     
44099     if(Roo.isIE){
44100         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44101     }
44102     
44103     // finally - if tabs are at the top, then create the body last..
44104     if(this.tabPosition != "bottom"){
44105         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44106          * @type Roo.Element
44107          */
44108         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44109         this.el.addClass("roo-tabs-top");
44110     }
44111     this.items = [];
44112
44113     this.bodyEl.setStyle("position", "relative");
44114
44115     this.active = null;
44116     this.activateDelegate = this.activate.createDelegate(this);
44117
44118     this.addEvents({
44119         /**
44120          * @event tabchange
44121          * Fires when the active tab changes
44122          * @param {Roo.TabPanel} this
44123          * @param {Roo.TabPanelItem} activePanel The new active tab
44124          */
44125         "tabchange": true,
44126         /**
44127          * @event beforetabchange
44128          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44129          * @param {Roo.TabPanel} this
44130          * @param {Object} e Set cancel to true on this object to cancel the tab change
44131          * @param {Roo.TabPanelItem} tab The tab being changed to
44132          */
44133         "beforetabchange" : true
44134     });
44135
44136     Roo.EventManager.onWindowResize(this.onResize, this);
44137     this.cpad = this.el.getPadding("lr");
44138     this.hiddenCount = 0;
44139
44140
44141     // toolbar on the tabbar support...
44142     if (this.toolbar) {
44143         alert("no toolbar support yet");
44144         this.toolbar  = false;
44145         /*
44146         var tcfg = this.toolbar;
44147         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44148         this.toolbar = new Roo.Toolbar(tcfg);
44149         if (Roo.isSafari) {
44150             var tbl = tcfg.container.child('table', true);
44151             tbl.setAttribute('width', '100%');
44152         }
44153         */
44154         
44155     }
44156    
44157
44158
44159     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44160 };
44161
44162 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44163     /*
44164      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44165      */
44166     tabPosition : "top",
44167     /*
44168      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44169      */
44170     currentTabWidth : 0,
44171     /*
44172      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44173      */
44174     minTabWidth : 40,
44175     /*
44176      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44177      */
44178     maxTabWidth : 250,
44179     /*
44180      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44181      */
44182     preferredTabWidth : 175,
44183     /*
44184      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44185      */
44186     resizeTabs : false,
44187     /*
44188      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44189      */
44190     monitorResize : true,
44191     /*
44192      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44193      */
44194     toolbar : false,  // set by caller..
44195     
44196     region : false, /// set by caller
44197     
44198     disableTooltips : true, // not used yet...
44199
44200     /**
44201      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44202      * @param {String} id The id of the div to use <b>or create</b>
44203      * @param {String} text The text for the tab
44204      * @param {String} content (optional) Content to put in the TabPanelItem body
44205      * @param {Boolean} closable (optional) True to create a close icon on the tab
44206      * @return {Roo.TabPanelItem} The created TabPanelItem
44207      */
44208     addTab : function(id, text, content, closable, tpl)
44209     {
44210         var item = new Roo.bootstrap.panel.TabItem({
44211             panel: this,
44212             id : id,
44213             text : text,
44214             closable : closable,
44215             tpl : tpl
44216         });
44217         this.addTabItem(item);
44218         if(content){
44219             item.setContent(content);
44220         }
44221         return item;
44222     },
44223
44224     /**
44225      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44226      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44227      * @return {Roo.TabPanelItem}
44228      */
44229     getTab : function(id){
44230         return this.items[id];
44231     },
44232
44233     /**
44234      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44235      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44236      */
44237     hideTab : function(id){
44238         var t = this.items[id];
44239         if(!t.isHidden()){
44240            t.setHidden(true);
44241            this.hiddenCount++;
44242            this.autoSizeTabs();
44243         }
44244     },
44245
44246     /**
44247      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44248      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44249      */
44250     unhideTab : function(id){
44251         var t = this.items[id];
44252         if(t.isHidden()){
44253            t.setHidden(false);
44254            this.hiddenCount--;
44255            this.autoSizeTabs();
44256         }
44257     },
44258
44259     /**
44260      * Adds an existing {@link Roo.TabPanelItem}.
44261      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44262      */
44263     addTabItem : function(item)
44264     {
44265         this.items[item.id] = item;
44266         this.items.push(item);
44267         this.autoSizeTabs();
44268       //  if(this.resizeTabs){
44269     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44270   //         this.autoSizeTabs();
44271 //        }else{
44272 //            item.autoSize();
44273        // }
44274     },
44275
44276     /**
44277      * Removes a {@link Roo.TabPanelItem}.
44278      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44279      */
44280     removeTab : function(id){
44281         var items = this.items;
44282         var tab = items[id];
44283         if(!tab) { return; }
44284         var index = items.indexOf(tab);
44285         if(this.active == tab && items.length > 1){
44286             var newTab = this.getNextAvailable(index);
44287             if(newTab) {
44288                 newTab.activate();
44289             }
44290         }
44291         this.stripEl.dom.removeChild(tab.pnode.dom);
44292         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44293             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44294         }
44295         items.splice(index, 1);
44296         delete this.items[tab.id];
44297         tab.fireEvent("close", tab);
44298         tab.purgeListeners();
44299         this.autoSizeTabs();
44300     },
44301
44302     getNextAvailable : function(start){
44303         var items = this.items;
44304         var index = start;
44305         // look for a next tab that will slide over to
44306         // replace the one being removed
44307         while(index < items.length){
44308             var item = items[++index];
44309             if(item && !item.isHidden()){
44310                 return item;
44311             }
44312         }
44313         // if one isn't found select the previous tab (on the left)
44314         index = start;
44315         while(index >= 0){
44316             var item = items[--index];
44317             if(item && !item.isHidden()){
44318                 return item;
44319             }
44320         }
44321         return null;
44322     },
44323
44324     /**
44325      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44326      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44327      */
44328     disableTab : function(id){
44329         var tab = this.items[id];
44330         if(tab && this.active != tab){
44331             tab.disable();
44332         }
44333     },
44334
44335     /**
44336      * Enables a {@link Roo.TabPanelItem} that is disabled.
44337      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44338      */
44339     enableTab : function(id){
44340         var tab = this.items[id];
44341         tab.enable();
44342     },
44343
44344     /**
44345      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44346      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44347      * @return {Roo.TabPanelItem} The TabPanelItem.
44348      */
44349     activate : function(id)
44350     {
44351         //Roo.log('activite:'  + id);
44352         
44353         var tab = this.items[id];
44354         if(!tab){
44355             return null;
44356         }
44357         if(tab == this.active || tab.disabled){
44358             return tab;
44359         }
44360         var e = {};
44361         this.fireEvent("beforetabchange", this, e, tab);
44362         if(e.cancel !== true && !tab.disabled){
44363             if(this.active){
44364                 this.active.hide();
44365             }
44366             this.active = this.items[id];
44367             this.active.show();
44368             this.fireEvent("tabchange", this, this.active);
44369         }
44370         return tab;
44371     },
44372
44373     /**
44374      * Gets the active {@link Roo.TabPanelItem}.
44375      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44376      */
44377     getActiveTab : function(){
44378         return this.active;
44379     },
44380
44381     /**
44382      * Updates the tab body element to fit the height of the container element
44383      * for overflow scrolling
44384      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44385      */
44386     syncHeight : function(targetHeight){
44387         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44388         var bm = this.bodyEl.getMargins();
44389         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44390         this.bodyEl.setHeight(newHeight);
44391         return newHeight;
44392     },
44393
44394     onResize : function(){
44395         if(this.monitorResize){
44396             this.autoSizeTabs();
44397         }
44398     },
44399
44400     /**
44401      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44402      */
44403     beginUpdate : function(){
44404         this.updating = true;
44405     },
44406
44407     /**
44408      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44409      */
44410     endUpdate : function(){
44411         this.updating = false;
44412         this.autoSizeTabs();
44413     },
44414
44415     /**
44416      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44417      */
44418     autoSizeTabs : function()
44419     {
44420         var count = this.items.length;
44421         var vcount = count - this.hiddenCount;
44422         
44423         if (vcount < 2) {
44424             this.stripEl.hide();
44425         } else {
44426             this.stripEl.show();
44427         }
44428         
44429         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44430             return;
44431         }
44432         
44433         
44434         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44435         var availWidth = Math.floor(w / vcount);
44436         var b = this.stripBody;
44437         if(b.getWidth() > w){
44438             var tabs = this.items;
44439             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44440             if(availWidth < this.minTabWidth){
44441                 /*if(!this.sleft){    // incomplete scrolling code
44442                     this.createScrollButtons();
44443                 }
44444                 this.showScroll();
44445                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44446             }
44447         }else{
44448             if(this.currentTabWidth < this.preferredTabWidth){
44449                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44450             }
44451         }
44452     },
44453
44454     /**
44455      * Returns the number of tabs in this TabPanel.
44456      * @return {Number}
44457      */
44458      getCount : function(){
44459          return this.items.length;
44460      },
44461
44462     /**
44463      * Resizes all the tabs to the passed width
44464      * @param {Number} The new width
44465      */
44466     setTabWidth : function(width){
44467         this.currentTabWidth = width;
44468         for(var i = 0, len = this.items.length; i < len; i++) {
44469                 if(!this.items[i].isHidden()) {
44470                 this.items[i].setWidth(width);
44471             }
44472         }
44473     },
44474
44475     /**
44476      * Destroys this TabPanel
44477      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44478      */
44479     destroy : function(removeEl){
44480         Roo.EventManager.removeResizeListener(this.onResize, this);
44481         for(var i = 0, len = this.items.length; i < len; i++){
44482             this.items[i].purgeListeners();
44483         }
44484         if(removeEl === true){
44485             this.el.update("");
44486             this.el.remove();
44487         }
44488     },
44489     
44490     createStrip : function(container)
44491     {
44492         var strip = document.createElement("nav");
44493         strip.className = Roo.bootstrap.version == 4 ?
44494             "navbar-light bg-light" : 
44495             "navbar navbar-default"; //"x-tabs-wrap";
44496         container.appendChild(strip);
44497         return strip;
44498     },
44499     
44500     createStripList : function(strip)
44501     {
44502         // div wrapper for retard IE
44503         // returns the "tr" element.
44504         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44505         //'<div class="x-tabs-strip-wrap">'+
44506           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44507           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44508         return strip.firstChild; //.firstChild.firstChild.firstChild;
44509     },
44510     createBody : function(container)
44511     {
44512         var body = document.createElement("div");
44513         Roo.id(body, "tab-body");
44514         //Roo.fly(body).addClass("x-tabs-body");
44515         Roo.fly(body).addClass("tab-content");
44516         container.appendChild(body);
44517         return body;
44518     },
44519     createItemBody :function(bodyEl, id){
44520         var body = Roo.getDom(id);
44521         if(!body){
44522             body = document.createElement("div");
44523             body.id = id;
44524         }
44525         //Roo.fly(body).addClass("x-tabs-item-body");
44526         Roo.fly(body).addClass("tab-pane");
44527          bodyEl.insertBefore(body, bodyEl.firstChild);
44528         return body;
44529     },
44530     /** @private */
44531     createStripElements :  function(stripEl, text, closable, tpl)
44532     {
44533         var td = document.createElement("li"); // was td..
44534         td.className = 'nav-item';
44535         
44536         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44537         
44538         
44539         stripEl.appendChild(td);
44540         /*if(closable){
44541             td.className = "x-tabs-closable";
44542             if(!this.closeTpl){
44543                 this.closeTpl = new Roo.Template(
44544                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44545                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44546                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44547                 );
44548             }
44549             var el = this.closeTpl.overwrite(td, {"text": text});
44550             var close = el.getElementsByTagName("div")[0];
44551             var inner = el.getElementsByTagName("em")[0];
44552             return {"el": el, "close": close, "inner": inner};
44553         } else {
44554         */
44555         // not sure what this is..
44556 //            if(!this.tabTpl){
44557                 //this.tabTpl = new Roo.Template(
44558                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44559                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44560                 //);
44561 //                this.tabTpl = new Roo.Template(
44562 //                   '<a href="#">' +
44563 //                   '<span unselectable="on"' +
44564 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44565 //                            ' >{text}</span></a>'
44566 //                );
44567 //                
44568 //            }
44569
44570
44571             var template = tpl || this.tabTpl || false;
44572             
44573             if(!template){
44574                 template =  new Roo.Template(
44575                         Roo.bootstrap.version == 4 ? 
44576                             (
44577                                 '<a class="nav-link" href="#" unselectable="on"' +
44578                                      (this.disableTooltips ? '' : ' title="{text}"') +
44579                                      ' >{text}</a>'
44580                             ) : (
44581                                 '<a class="nav-link" href="#">' +
44582                                 '<span unselectable="on"' +
44583                                          (this.disableTooltips ? '' : ' title="{text}"') +
44584                                     ' >{text}</span></a>'
44585                             )
44586                 );
44587             }
44588             
44589             switch (typeof(template)) {
44590                 case 'object' :
44591                     break;
44592                 case 'string' :
44593                     template = new Roo.Template(template);
44594                     break;
44595                 default :
44596                     break;
44597             }
44598             
44599             var el = template.overwrite(td, {"text": text});
44600             
44601             var inner = el.getElementsByTagName("span")[0];
44602             
44603             return {"el": el, "inner": inner};
44604             
44605     }
44606         
44607     
44608 });
44609
44610 /**
44611  * @class Roo.TabPanelItem
44612  * @extends Roo.util.Observable
44613  * Represents an individual item (tab plus body) in a TabPanel.
44614  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44615  * @param {String} id The id of this TabPanelItem
44616  * @param {String} text The text for the tab of this TabPanelItem
44617  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44618  */
44619 Roo.bootstrap.panel.TabItem = function(config){
44620     /**
44621      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44622      * @type Roo.TabPanel
44623      */
44624     this.tabPanel = config.panel;
44625     /**
44626      * The id for this TabPanelItem
44627      * @type String
44628      */
44629     this.id = config.id;
44630     /** @private */
44631     this.disabled = false;
44632     /** @private */
44633     this.text = config.text;
44634     /** @private */
44635     this.loaded = false;
44636     this.closable = config.closable;
44637
44638     /**
44639      * The body element for this TabPanelItem.
44640      * @type Roo.Element
44641      */
44642     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44643     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44644     this.bodyEl.setStyle("display", "block");
44645     this.bodyEl.setStyle("zoom", "1");
44646     //this.hideAction();
44647
44648     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44649     /** @private */
44650     this.el = Roo.get(els.el);
44651     this.inner = Roo.get(els.inner, true);
44652      this.textEl = Roo.bootstrap.version == 4 ?
44653         this.el : Roo.get(this.el.dom.firstChild, true);
44654
44655     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44656     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44657
44658     
44659 //    this.el.on("mousedown", this.onTabMouseDown, this);
44660     this.el.on("click", this.onTabClick, this);
44661     /** @private */
44662     if(config.closable){
44663         var c = Roo.get(els.close, true);
44664         c.dom.title = this.closeText;
44665         c.addClassOnOver("close-over");
44666         c.on("click", this.closeClick, this);
44667      }
44668
44669     this.addEvents({
44670          /**
44671          * @event activate
44672          * Fires when this tab becomes the active tab.
44673          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44674          * @param {Roo.TabPanelItem} this
44675          */
44676         "activate": true,
44677         /**
44678          * @event beforeclose
44679          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44680          * @param {Roo.TabPanelItem} this
44681          * @param {Object} e Set cancel to true on this object to cancel the close.
44682          */
44683         "beforeclose": true,
44684         /**
44685          * @event close
44686          * Fires when this tab is closed.
44687          * @param {Roo.TabPanelItem} this
44688          */
44689          "close": true,
44690         /**
44691          * @event deactivate
44692          * Fires when this tab is no longer the active tab.
44693          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44694          * @param {Roo.TabPanelItem} this
44695          */
44696          "deactivate" : true
44697     });
44698     this.hidden = false;
44699
44700     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44701 };
44702
44703 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44704            {
44705     purgeListeners : function(){
44706        Roo.util.Observable.prototype.purgeListeners.call(this);
44707        this.el.removeAllListeners();
44708     },
44709     /**
44710      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44711      */
44712     show : function(){
44713         this.status_node.addClass("active");
44714         this.showAction();
44715         if(Roo.isOpera){
44716             this.tabPanel.stripWrap.repaint();
44717         }
44718         this.fireEvent("activate", this.tabPanel, this);
44719     },
44720
44721     /**
44722      * Returns true if this tab is the active tab.
44723      * @return {Boolean}
44724      */
44725     isActive : function(){
44726         return this.tabPanel.getActiveTab() == this;
44727     },
44728
44729     /**
44730      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44731      */
44732     hide : function(){
44733         this.status_node.removeClass("active");
44734         this.hideAction();
44735         this.fireEvent("deactivate", this.tabPanel, this);
44736     },
44737
44738     hideAction : function(){
44739         this.bodyEl.hide();
44740         this.bodyEl.setStyle("position", "absolute");
44741         this.bodyEl.setLeft("-20000px");
44742         this.bodyEl.setTop("-20000px");
44743     },
44744
44745     showAction : function(){
44746         this.bodyEl.setStyle("position", "relative");
44747         this.bodyEl.setTop("");
44748         this.bodyEl.setLeft("");
44749         this.bodyEl.show();
44750     },
44751
44752     /**
44753      * Set the tooltip for the tab.
44754      * @param {String} tooltip The tab's tooltip
44755      */
44756     setTooltip : function(text){
44757         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44758             this.textEl.dom.qtip = text;
44759             this.textEl.dom.removeAttribute('title');
44760         }else{
44761             this.textEl.dom.title = text;
44762         }
44763     },
44764
44765     onTabClick : function(e){
44766         e.preventDefault();
44767         this.tabPanel.activate(this.id);
44768     },
44769
44770     onTabMouseDown : function(e){
44771         e.preventDefault();
44772         this.tabPanel.activate(this.id);
44773     },
44774 /*
44775     getWidth : function(){
44776         return this.inner.getWidth();
44777     },
44778
44779     setWidth : function(width){
44780         var iwidth = width - this.linode.getPadding("lr");
44781         this.inner.setWidth(iwidth);
44782         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44783         this.linode.setWidth(width);
44784     },
44785 */
44786     /**
44787      * Show or hide the tab
44788      * @param {Boolean} hidden True to hide or false to show.
44789      */
44790     setHidden : function(hidden){
44791         this.hidden = hidden;
44792         this.linode.setStyle("display", hidden ? "none" : "");
44793     },
44794
44795     /**
44796      * Returns true if this tab is "hidden"
44797      * @return {Boolean}
44798      */
44799     isHidden : function(){
44800         return this.hidden;
44801     },
44802
44803     /**
44804      * Returns the text for this tab
44805      * @return {String}
44806      */
44807     getText : function(){
44808         return this.text;
44809     },
44810     /*
44811     autoSize : function(){
44812         //this.el.beginMeasure();
44813         this.textEl.setWidth(1);
44814         /*
44815          *  #2804 [new] Tabs in Roojs
44816          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44817          */
44818         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44819         //this.el.endMeasure();
44820     //},
44821
44822     /**
44823      * Sets the text for the tab (Note: this also sets the tooltip text)
44824      * @param {String} text The tab's text and tooltip
44825      */
44826     setText : function(text){
44827         this.text = text;
44828         this.textEl.update(text);
44829         this.setTooltip(text);
44830         //if(!this.tabPanel.resizeTabs){
44831         //    this.autoSize();
44832         //}
44833     },
44834     /**
44835      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44836      */
44837     activate : function(){
44838         this.tabPanel.activate(this.id);
44839     },
44840
44841     /**
44842      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44843      */
44844     disable : function(){
44845         if(this.tabPanel.active != this){
44846             this.disabled = true;
44847             this.status_node.addClass("disabled");
44848         }
44849     },
44850
44851     /**
44852      * Enables this TabPanelItem if it was previously disabled.
44853      */
44854     enable : function(){
44855         this.disabled = false;
44856         this.status_node.removeClass("disabled");
44857     },
44858
44859     /**
44860      * Sets the content for this TabPanelItem.
44861      * @param {String} content The content
44862      * @param {Boolean} loadScripts true to look for and load scripts
44863      */
44864     setContent : function(content, loadScripts){
44865         this.bodyEl.update(content, loadScripts);
44866     },
44867
44868     /**
44869      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44870      * @return {Roo.UpdateManager} The UpdateManager
44871      */
44872     getUpdateManager : function(){
44873         return this.bodyEl.getUpdateManager();
44874     },
44875
44876     /**
44877      * Set a URL to be used to load the content for this TabPanelItem.
44878      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44879      * @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)
44880      * @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)
44881      * @return {Roo.UpdateManager} The UpdateManager
44882      */
44883     setUrl : function(url, params, loadOnce){
44884         if(this.refreshDelegate){
44885             this.un('activate', this.refreshDelegate);
44886         }
44887         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44888         this.on("activate", this.refreshDelegate);
44889         return this.bodyEl.getUpdateManager();
44890     },
44891
44892     /** @private */
44893     _handleRefresh : function(url, params, loadOnce){
44894         if(!loadOnce || !this.loaded){
44895             var updater = this.bodyEl.getUpdateManager();
44896             updater.update(url, params, this._setLoaded.createDelegate(this));
44897         }
44898     },
44899
44900     /**
44901      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44902      *   Will fail silently if the setUrl method has not been called.
44903      *   This does not activate the panel, just updates its content.
44904      */
44905     refresh : function(){
44906         if(this.refreshDelegate){
44907            this.loaded = false;
44908            this.refreshDelegate();
44909         }
44910     },
44911
44912     /** @private */
44913     _setLoaded : function(){
44914         this.loaded = true;
44915     },
44916
44917     /** @private */
44918     closeClick : function(e){
44919         var o = {};
44920         e.stopEvent();
44921         this.fireEvent("beforeclose", this, o);
44922         if(o.cancel !== true){
44923             this.tabPanel.removeTab(this.id);
44924         }
44925     },
44926     /**
44927      * The text displayed in the tooltip for the close icon.
44928      * @type String
44929      */
44930     closeText : "Close this tab"
44931 });
44932 /**
44933 *    This script refer to:
44934 *    Title: International Telephone Input
44935 *    Author: Jack O'Connor
44936 *    Code version:  v12.1.12
44937 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44938 **/
44939
44940 Roo.bootstrap.form.PhoneInputData = function() {
44941     var d = [
44942       [
44943         "Afghanistan (‫افغانستان‬‎)",
44944         "af",
44945         "93"
44946       ],
44947       [
44948         "Albania (Shqipëri)",
44949         "al",
44950         "355"
44951       ],
44952       [
44953         "Algeria (‫الجزائر‬‎)",
44954         "dz",
44955         "213"
44956       ],
44957       [
44958         "American Samoa",
44959         "as",
44960         "1684"
44961       ],
44962       [
44963         "Andorra",
44964         "ad",
44965         "376"
44966       ],
44967       [
44968         "Angola",
44969         "ao",
44970         "244"
44971       ],
44972       [
44973         "Anguilla",
44974         "ai",
44975         "1264"
44976       ],
44977       [
44978         "Antigua and Barbuda",
44979         "ag",
44980         "1268"
44981       ],
44982       [
44983         "Argentina",
44984         "ar",
44985         "54"
44986       ],
44987       [
44988         "Armenia (Հայաստան)",
44989         "am",
44990         "374"
44991       ],
44992       [
44993         "Aruba",
44994         "aw",
44995         "297"
44996       ],
44997       [
44998         "Australia",
44999         "au",
45000         "61",
45001         0
45002       ],
45003       [
45004         "Austria (Österreich)",
45005         "at",
45006         "43"
45007       ],
45008       [
45009         "Azerbaijan (Azərbaycan)",
45010         "az",
45011         "994"
45012       ],
45013       [
45014         "Bahamas",
45015         "bs",
45016         "1242"
45017       ],
45018       [
45019         "Bahrain (‫البحرين‬‎)",
45020         "bh",
45021         "973"
45022       ],
45023       [
45024         "Bangladesh (বাংলাদেশ)",
45025         "bd",
45026         "880"
45027       ],
45028       [
45029         "Barbados",
45030         "bb",
45031         "1246"
45032       ],
45033       [
45034         "Belarus (Беларусь)",
45035         "by",
45036         "375"
45037       ],
45038       [
45039         "Belgium (België)",
45040         "be",
45041         "32"
45042       ],
45043       [
45044         "Belize",
45045         "bz",
45046         "501"
45047       ],
45048       [
45049         "Benin (Bénin)",
45050         "bj",
45051         "229"
45052       ],
45053       [
45054         "Bermuda",
45055         "bm",
45056         "1441"
45057       ],
45058       [
45059         "Bhutan (འབྲུག)",
45060         "bt",
45061         "975"
45062       ],
45063       [
45064         "Bolivia",
45065         "bo",
45066         "591"
45067       ],
45068       [
45069         "Bosnia and Herzegovina (Босна и Херцеговина)",
45070         "ba",
45071         "387"
45072       ],
45073       [
45074         "Botswana",
45075         "bw",
45076         "267"
45077       ],
45078       [
45079         "Brazil (Brasil)",
45080         "br",
45081         "55"
45082       ],
45083       [
45084         "British Indian Ocean Territory",
45085         "io",
45086         "246"
45087       ],
45088       [
45089         "British Virgin Islands",
45090         "vg",
45091         "1284"
45092       ],
45093       [
45094         "Brunei",
45095         "bn",
45096         "673"
45097       ],
45098       [
45099         "Bulgaria (България)",
45100         "bg",
45101         "359"
45102       ],
45103       [
45104         "Burkina Faso",
45105         "bf",
45106         "226"
45107       ],
45108       [
45109         "Burundi (Uburundi)",
45110         "bi",
45111         "257"
45112       ],
45113       [
45114         "Cambodia (កម្ពុជា)",
45115         "kh",
45116         "855"
45117       ],
45118       [
45119         "Cameroon (Cameroun)",
45120         "cm",
45121         "237"
45122       ],
45123       [
45124         "Canada",
45125         "ca",
45126         "1",
45127         1,
45128         ["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"]
45129       ],
45130       [
45131         "Cape Verde (Kabu Verdi)",
45132         "cv",
45133         "238"
45134       ],
45135       [
45136         "Caribbean Netherlands",
45137         "bq",
45138         "599",
45139         1
45140       ],
45141       [
45142         "Cayman Islands",
45143         "ky",
45144         "1345"
45145       ],
45146       [
45147         "Central African Republic (République centrafricaine)",
45148         "cf",
45149         "236"
45150       ],
45151       [
45152         "Chad (Tchad)",
45153         "td",
45154         "235"
45155       ],
45156       [
45157         "Chile",
45158         "cl",
45159         "56"
45160       ],
45161       [
45162         "China (中国)",
45163         "cn",
45164         "86"
45165       ],
45166       [
45167         "Christmas Island",
45168         "cx",
45169         "61",
45170         2
45171       ],
45172       [
45173         "Cocos (Keeling) Islands",
45174         "cc",
45175         "61",
45176         1
45177       ],
45178       [
45179         "Colombia",
45180         "co",
45181         "57"
45182       ],
45183       [
45184         "Comoros (‫جزر القمر‬‎)",
45185         "km",
45186         "269"
45187       ],
45188       [
45189         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45190         "cd",
45191         "243"
45192       ],
45193       [
45194         "Congo (Republic) (Congo-Brazzaville)",
45195         "cg",
45196         "242"
45197       ],
45198       [
45199         "Cook Islands",
45200         "ck",
45201         "682"
45202       ],
45203       [
45204         "Costa Rica",
45205         "cr",
45206         "506"
45207       ],
45208       [
45209         "Côte d’Ivoire",
45210         "ci",
45211         "225"
45212       ],
45213       [
45214         "Croatia (Hrvatska)",
45215         "hr",
45216         "385"
45217       ],
45218       [
45219         "Cuba",
45220         "cu",
45221         "53"
45222       ],
45223       [
45224         "Curaçao",
45225         "cw",
45226         "599",
45227         0
45228       ],
45229       [
45230         "Cyprus (Κύπρος)",
45231         "cy",
45232         "357"
45233       ],
45234       [
45235         "Czech Republic (Česká republika)",
45236         "cz",
45237         "420"
45238       ],
45239       [
45240         "Denmark (Danmark)",
45241         "dk",
45242         "45"
45243       ],
45244       [
45245         "Djibouti",
45246         "dj",
45247         "253"
45248       ],
45249       [
45250         "Dominica",
45251         "dm",
45252         "1767"
45253       ],
45254       [
45255         "Dominican Republic (República Dominicana)",
45256         "do",
45257         "1",
45258         2,
45259         ["809", "829", "849"]
45260       ],
45261       [
45262         "Ecuador",
45263         "ec",
45264         "593"
45265       ],
45266       [
45267         "Egypt (‫مصر‬‎)",
45268         "eg",
45269         "20"
45270       ],
45271       [
45272         "El Salvador",
45273         "sv",
45274         "503"
45275       ],
45276       [
45277         "Equatorial Guinea (Guinea Ecuatorial)",
45278         "gq",
45279         "240"
45280       ],
45281       [
45282         "Eritrea",
45283         "er",
45284         "291"
45285       ],
45286       [
45287         "Estonia (Eesti)",
45288         "ee",
45289         "372"
45290       ],
45291       [
45292         "Ethiopia",
45293         "et",
45294         "251"
45295       ],
45296       [
45297         "Falkland Islands (Islas Malvinas)",
45298         "fk",
45299         "500"
45300       ],
45301       [
45302         "Faroe Islands (Føroyar)",
45303         "fo",
45304         "298"
45305       ],
45306       [
45307         "Fiji",
45308         "fj",
45309         "679"
45310       ],
45311       [
45312         "Finland (Suomi)",
45313         "fi",
45314         "358",
45315         0
45316       ],
45317       [
45318         "France",
45319         "fr",
45320         "33"
45321       ],
45322       [
45323         "French Guiana (Guyane française)",
45324         "gf",
45325         "594"
45326       ],
45327       [
45328         "French Polynesia (Polynésie française)",
45329         "pf",
45330         "689"
45331       ],
45332       [
45333         "Gabon",
45334         "ga",
45335         "241"
45336       ],
45337       [
45338         "Gambia",
45339         "gm",
45340         "220"
45341       ],
45342       [
45343         "Georgia (საქართველო)",
45344         "ge",
45345         "995"
45346       ],
45347       [
45348         "Germany (Deutschland)",
45349         "de",
45350         "49"
45351       ],
45352       [
45353         "Ghana (Gaana)",
45354         "gh",
45355         "233"
45356       ],
45357       [
45358         "Gibraltar",
45359         "gi",
45360         "350"
45361       ],
45362       [
45363         "Greece (Ελλάδα)",
45364         "gr",
45365         "30"
45366       ],
45367       [
45368         "Greenland (Kalaallit Nunaat)",
45369         "gl",
45370         "299"
45371       ],
45372       [
45373         "Grenada",
45374         "gd",
45375         "1473"
45376       ],
45377       [
45378         "Guadeloupe",
45379         "gp",
45380         "590",
45381         0
45382       ],
45383       [
45384         "Guam",
45385         "gu",
45386         "1671"
45387       ],
45388       [
45389         "Guatemala",
45390         "gt",
45391         "502"
45392       ],
45393       [
45394         "Guernsey",
45395         "gg",
45396         "44",
45397         1
45398       ],
45399       [
45400         "Guinea (Guinée)",
45401         "gn",
45402         "224"
45403       ],
45404       [
45405         "Guinea-Bissau (Guiné Bissau)",
45406         "gw",
45407         "245"
45408       ],
45409       [
45410         "Guyana",
45411         "gy",
45412         "592"
45413       ],
45414       [
45415         "Haiti",
45416         "ht",
45417         "509"
45418       ],
45419       [
45420         "Honduras",
45421         "hn",
45422         "504"
45423       ],
45424       [
45425         "Hong Kong (香港)",
45426         "hk",
45427         "852"
45428       ],
45429       [
45430         "Hungary (Magyarország)",
45431         "hu",
45432         "36"
45433       ],
45434       [
45435         "Iceland (Ísland)",
45436         "is",
45437         "354"
45438       ],
45439       [
45440         "India (भारत)",
45441         "in",
45442         "91"
45443       ],
45444       [
45445         "Indonesia",
45446         "id",
45447         "62"
45448       ],
45449       [
45450         "Iran (‫ایران‬‎)",
45451         "ir",
45452         "98"
45453       ],
45454       [
45455         "Iraq (‫العراق‬‎)",
45456         "iq",
45457         "964"
45458       ],
45459       [
45460         "Ireland",
45461         "ie",
45462         "353"
45463       ],
45464       [
45465         "Isle of Man",
45466         "im",
45467         "44",
45468         2
45469       ],
45470       [
45471         "Israel (‫ישראל‬‎)",
45472         "il",
45473         "972"
45474       ],
45475       [
45476         "Italy (Italia)",
45477         "it",
45478         "39",
45479         0
45480       ],
45481       [
45482         "Jamaica",
45483         "jm",
45484         "1876"
45485       ],
45486       [
45487         "Japan (日本)",
45488         "jp",
45489         "81"
45490       ],
45491       [
45492         "Jersey",
45493         "je",
45494         "44",
45495         3
45496       ],
45497       [
45498         "Jordan (‫الأردن‬‎)",
45499         "jo",
45500         "962"
45501       ],
45502       [
45503         "Kazakhstan (Казахстан)",
45504         "kz",
45505         "7",
45506         1
45507       ],
45508       [
45509         "Kenya",
45510         "ke",
45511         "254"
45512       ],
45513       [
45514         "Kiribati",
45515         "ki",
45516         "686"
45517       ],
45518       [
45519         "Kosovo",
45520         "xk",
45521         "383"
45522       ],
45523       [
45524         "Kuwait (‫الكويت‬‎)",
45525         "kw",
45526         "965"
45527       ],
45528       [
45529         "Kyrgyzstan (Кыргызстан)",
45530         "kg",
45531         "996"
45532       ],
45533       [
45534         "Laos (ລາວ)",
45535         "la",
45536         "856"
45537       ],
45538       [
45539         "Latvia (Latvija)",
45540         "lv",
45541         "371"
45542       ],
45543       [
45544         "Lebanon (‫لبنان‬‎)",
45545         "lb",
45546         "961"
45547       ],
45548       [
45549         "Lesotho",
45550         "ls",
45551         "266"
45552       ],
45553       [
45554         "Liberia",
45555         "lr",
45556         "231"
45557       ],
45558       [
45559         "Libya (‫ليبيا‬‎)",
45560         "ly",
45561         "218"
45562       ],
45563       [
45564         "Liechtenstein",
45565         "li",
45566         "423"
45567       ],
45568       [
45569         "Lithuania (Lietuva)",
45570         "lt",
45571         "370"
45572       ],
45573       [
45574         "Luxembourg",
45575         "lu",
45576         "352"
45577       ],
45578       [
45579         "Macau (澳門)",
45580         "mo",
45581         "853"
45582       ],
45583       [
45584         "Macedonia (FYROM) (Македонија)",
45585         "mk",
45586         "389"
45587       ],
45588       [
45589         "Madagascar (Madagasikara)",
45590         "mg",
45591         "261"
45592       ],
45593       [
45594         "Malawi",
45595         "mw",
45596         "265"
45597       ],
45598       [
45599         "Malaysia",
45600         "my",
45601         "60"
45602       ],
45603       [
45604         "Maldives",
45605         "mv",
45606         "960"
45607       ],
45608       [
45609         "Mali",
45610         "ml",
45611         "223"
45612       ],
45613       [
45614         "Malta",
45615         "mt",
45616         "356"
45617       ],
45618       [
45619         "Marshall Islands",
45620         "mh",
45621         "692"
45622       ],
45623       [
45624         "Martinique",
45625         "mq",
45626         "596"
45627       ],
45628       [
45629         "Mauritania (‫موريتانيا‬‎)",
45630         "mr",
45631         "222"
45632       ],
45633       [
45634         "Mauritius (Moris)",
45635         "mu",
45636         "230"
45637       ],
45638       [
45639         "Mayotte",
45640         "yt",
45641         "262",
45642         1
45643       ],
45644       [
45645         "Mexico (México)",
45646         "mx",
45647         "52"
45648       ],
45649       [
45650         "Micronesia",
45651         "fm",
45652         "691"
45653       ],
45654       [
45655         "Moldova (Republica Moldova)",
45656         "md",
45657         "373"
45658       ],
45659       [
45660         "Monaco",
45661         "mc",
45662         "377"
45663       ],
45664       [
45665         "Mongolia (Монгол)",
45666         "mn",
45667         "976"
45668       ],
45669       [
45670         "Montenegro (Crna Gora)",
45671         "me",
45672         "382"
45673       ],
45674       [
45675         "Montserrat",
45676         "ms",
45677         "1664"
45678       ],
45679       [
45680         "Morocco (‫المغرب‬‎)",
45681         "ma",
45682         "212",
45683         0
45684       ],
45685       [
45686         "Mozambique (Moçambique)",
45687         "mz",
45688         "258"
45689       ],
45690       [
45691         "Myanmar (Burma) (မြန်မာ)",
45692         "mm",
45693         "95"
45694       ],
45695       [
45696         "Namibia (Namibië)",
45697         "na",
45698         "264"
45699       ],
45700       [
45701         "Nauru",
45702         "nr",
45703         "674"
45704       ],
45705       [
45706         "Nepal (नेपाल)",
45707         "np",
45708         "977"
45709       ],
45710       [
45711         "Netherlands (Nederland)",
45712         "nl",
45713         "31"
45714       ],
45715       [
45716         "New Caledonia (Nouvelle-Calédonie)",
45717         "nc",
45718         "687"
45719       ],
45720       [
45721         "New Zealand",
45722         "nz",
45723         "64"
45724       ],
45725       [
45726         "Nicaragua",
45727         "ni",
45728         "505"
45729       ],
45730       [
45731         "Niger (Nijar)",
45732         "ne",
45733         "227"
45734       ],
45735       [
45736         "Nigeria",
45737         "ng",
45738         "234"
45739       ],
45740       [
45741         "Niue",
45742         "nu",
45743         "683"
45744       ],
45745       [
45746         "Norfolk Island",
45747         "nf",
45748         "672"
45749       ],
45750       [
45751         "North Korea (조선 민주주의 인민 공화국)",
45752         "kp",
45753         "850"
45754       ],
45755       [
45756         "Northern Mariana Islands",
45757         "mp",
45758         "1670"
45759       ],
45760       [
45761         "Norway (Norge)",
45762         "no",
45763         "47",
45764         0
45765       ],
45766       [
45767         "Oman (‫عُمان‬‎)",
45768         "om",
45769         "968"
45770       ],
45771       [
45772         "Pakistan (‫پاکستان‬‎)",
45773         "pk",
45774         "92"
45775       ],
45776       [
45777         "Palau",
45778         "pw",
45779         "680"
45780       ],
45781       [
45782         "Palestine (‫فلسطين‬‎)",
45783         "ps",
45784         "970"
45785       ],
45786       [
45787         "Panama (Panamá)",
45788         "pa",
45789         "507"
45790       ],
45791       [
45792         "Papua New Guinea",
45793         "pg",
45794         "675"
45795       ],
45796       [
45797         "Paraguay",
45798         "py",
45799         "595"
45800       ],
45801       [
45802         "Peru (Perú)",
45803         "pe",
45804         "51"
45805       ],
45806       [
45807         "Philippines",
45808         "ph",
45809         "63"
45810       ],
45811       [
45812         "Poland (Polska)",
45813         "pl",
45814         "48"
45815       ],
45816       [
45817         "Portugal",
45818         "pt",
45819         "351"
45820       ],
45821       [
45822         "Puerto Rico",
45823         "pr",
45824         "1",
45825         3,
45826         ["787", "939"]
45827       ],
45828       [
45829         "Qatar (‫قطر‬‎)",
45830         "qa",
45831         "974"
45832       ],
45833       [
45834         "Réunion (La Réunion)",
45835         "re",
45836         "262",
45837         0
45838       ],
45839       [
45840         "Romania (România)",
45841         "ro",
45842         "40"
45843       ],
45844       [
45845         "Russia (Россия)",
45846         "ru",
45847         "7",
45848         0
45849       ],
45850       [
45851         "Rwanda",
45852         "rw",
45853         "250"
45854       ],
45855       [
45856         "Saint Barthélemy",
45857         "bl",
45858         "590",
45859         1
45860       ],
45861       [
45862         "Saint Helena",
45863         "sh",
45864         "290"
45865       ],
45866       [
45867         "Saint Kitts and Nevis",
45868         "kn",
45869         "1869"
45870       ],
45871       [
45872         "Saint Lucia",
45873         "lc",
45874         "1758"
45875       ],
45876       [
45877         "Saint Martin (Saint-Martin (partie française))",
45878         "mf",
45879         "590",
45880         2
45881       ],
45882       [
45883         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45884         "pm",
45885         "508"
45886       ],
45887       [
45888         "Saint Vincent and the Grenadines",
45889         "vc",
45890         "1784"
45891       ],
45892       [
45893         "Samoa",
45894         "ws",
45895         "685"
45896       ],
45897       [
45898         "San Marino",
45899         "sm",
45900         "378"
45901       ],
45902       [
45903         "São Tomé and Príncipe (São Tomé e Príncipe)",
45904         "st",
45905         "239"
45906       ],
45907       [
45908         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45909         "sa",
45910         "966"
45911       ],
45912       [
45913         "Senegal (Sénégal)",
45914         "sn",
45915         "221"
45916       ],
45917       [
45918         "Serbia (Србија)",
45919         "rs",
45920         "381"
45921       ],
45922       [
45923         "Seychelles",
45924         "sc",
45925         "248"
45926       ],
45927       [
45928         "Sierra Leone",
45929         "sl",
45930         "232"
45931       ],
45932       [
45933         "Singapore",
45934         "sg",
45935         "65"
45936       ],
45937       [
45938         "Sint Maarten",
45939         "sx",
45940         "1721"
45941       ],
45942       [
45943         "Slovakia (Slovensko)",
45944         "sk",
45945         "421"
45946       ],
45947       [
45948         "Slovenia (Slovenija)",
45949         "si",
45950         "386"
45951       ],
45952       [
45953         "Solomon Islands",
45954         "sb",
45955         "677"
45956       ],
45957       [
45958         "Somalia (Soomaaliya)",
45959         "so",
45960         "252"
45961       ],
45962       [
45963         "South Africa",
45964         "za",
45965         "27"
45966       ],
45967       [
45968         "South Korea (대한민국)",
45969         "kr",
45970         "82"
45971       ],
45972       [
45973         "South Sudan (‫جنوب السودان‬‎)",
45974         "ss",
45975         "211"
45976       ],
45977       [
45978         "Spain (España)",
45979         "es",
45980         "34"
45981       ],
45982       [
45983         "Sri Lanka (ශ්‍රී ලංකාව)",
45984         "lk",
45985         "94"
45986       ],
45987       [
45988         "Sudan (‫السودان‬‎)",
45989         "sd",
45990         "249"
45991       ],
45992       [
45993         "Suriname",
45994         "sr",
45995         "597"
45996       ],
45997       [
45998         "Svalbard and Jan Mayen",
45999         "sj",
46000         "47",
46001         1
46002       ],
46003       [
46004         "Swaziland",
46005         "sz",
46006         "268"
46007       ],
46008       [
46009         "Sweden (Sverige)",
46010         "se",
46011         "46"
46012       ],
46013       [
46014         "Switzerland (Schweiz)",
46015         "ch",
46016         "41"
46017       ],
46018       [
46019         "Syria (‫سوريا‬‎)",
46020         "sy",
46021         "963"
46022       ],
46023       [
46024         "Taiwan (台灣)",
46025         "tw",
46026         "886"
46027       ],
46028       [
46029         "Tajikistan",
46030         "tj",
46031         "992"
46032       ],
46033       [
46034         "Tanzania",
46035         "tz",
46036         "255"
46037       ],
46038       [
46039         "Thailand (ไทย)",
46040         "th",
46041         "66"
46042       ],
46043       [
46044         "Timor-Leste",
46045         "tl",
46046         "670"
46047       ],
46048       [
46049         "Togo",
46050         "tg",
46051         "228"
46052       ],
46053       [
46054         "Tokelau",
46055         "tk",
46056         "690"
46057       ],
46058       [
46059         "Tonga",
46060         "to",
46061         "676"
46062       ],
46063       [
46064         "Trinidad and Tobago",
46065         "tt",
46066         "1868"
46067       ],
46068       [
46069         "Tunisia (‫تونس‬‎)",
46070         "tn",
46071         "216"
46072       ],
46073       [
46074         "Turkey (Türkiye)",
46075         "tr",
46076         "90"
46077       ],
46078       [
46079         "Turkmenistan",
46080         "tm",
46081         "993"
46082       ],
46083       [
46084         "Turks and Caicos Islands",
46085         "tc",
46086         "1649"
46087       ],
46088       [
46089         "Tuvalu",
46090         "tv",
46091         "688"
46092       ],
46093       [
46094         "U.S. Virgin Islands",
46095         "vi",
46096         "1340"
46097       ],
46098       [
46099         "Uganda",
46100         "ug",
46101         "256"
46102       ],
46103       [
46104         "Ukraine (Україна)",
46105         "ua",
46106         "380"
46107       ],
46108       [
46109         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46110         "ae",
46111         "971"
46112       ],
46113       [
46114         "United Kingdom",
46115         "gb",
46116         "44",
46117         0
46118       ],
46119       [
46120         "United States",
46121         "us",
46122         "1",
46123         0
46124       ],
46125       [
46126         "Uruguay",
46127         "uy",
46128         "598"
46129       ],
46130       [
46131         "Uzbekistan (Oʻzbekiston)",
46132         "uz",
46133         "998"
46134       ],
46135       [
46136         "Vanuatu",
46137         "vu",
46138         "678"
46139       ],
46140       [
46141         "Vatican City (Città del Vaticano)",
46142         "va",
46143         "39",
46144         1
46145       ],
46146       [
46147         "Venezuela",
46148         "ve",
46149         "58"
46150       ],
46151       [
46152         "Vietnam (Việt Nam)",
46153         "vn",
46154         "84"
46155       ],
46156       [
46157         "Wallis and Futuna (Wallis-et-Futuna)",
46158         "wf",
46159         "681"
46160       ],
46161       [
46162         "Western Sahara (‫الصحراء الغربية‬‎)",
46163         "eh",
46164         "212",
46165         1
46166       ],
46167       [
46168         "Yemen (‫اليمن‬‎)",
46169         "ye",
46170         "967"
46171       ],
46172       [
46173         "Zambia",
46174         "zm",
46175         "260"
46176       ],
46177       [
46178         "Zimbabwe",
46179         "zw",
46180         "263"
46181       ],
46182       [
46183         "Åland Islands",
46184         "ax",
46185         "358",
46186         1
46187       ]
46188   ];
46189   
46190   return d;
46191 }/**
46192 *    This script refer to:
46193 *    Title: International Telephone Input
46194 *    Author: Jack O'Connor
46195 *    Code version:  v12.1.12
46196 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46197 **/
46198
46199 /**
46200  * @class Roo.bootstrap.form.PhoneInput
46201  * @extends Roo.bootstrap.form.TriggerField
46202  * An input with International dial-code selection
46203  
46204  * @cfg {String} defaultDialCode default '+852'
46205  * @cfg {Array} preferedCountries default []
46206   
46207  * @constructor
46208  * Create a new PhoneInput.
46209  * @param {Object} config Configuration options
46210  */
46211
46212 Roo.bootstrap.form.PhoneInput = function(config) {
46213     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46214 };
46215
46216 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46217         /**
46218         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46219         */
46220         listWidth: undefined,
46221         
46222         selectedClass: 'active',
46223         
46224         invalidClass : "has-warning",
46225         
46226         validClass: 'has-success',
46227         
46228         allowed: '0123456789',
46229         
46230         max_length: 15,
46231         
46232         /**
46233          * @cfg {String} defaultDialCode The default dial code when initializing the input
46234          */
46235         defaultDialCode: '+852',
46236         
46237         /**
46238          * @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
46239          */
46240         preferedCountries: false,
46241         
46242         getAutoCreate : function()
46243         {
46244             var data = Roo.bootstrap.form.PhoneInputData();
46245             var align = this.labelAlign || this.parentLabelAlign();
46246             var id = Roo.id();
46247             
46248             this.allCountries = [];
46249             this.dialCodeMapping = [];
46250             
46251             for (var i = 0; i < data.length; i++) {
46252               var c = data[i];
46253               this.allCountries[i] = {
46254                 name: c[0],
46255                 iso2: c[1],
46256                 dialCode: c[2],
46257                 priority: c[3] || 0,
46258                 areaCodes: c[4] || null
46259               };
46260               this.dialCodeMapping[c[2]] = {
46261                   name: c[0],
46262                   iso2: c[1],
46263                   priority: c[3] || 0,
46264                   areaCodes: c[4] || null
46265               };
46266             }
46267             
46268             var cfg = {
46269                 cls: 'form-group',
46270                 cn: []
46271             };
46272             
46273             var input =  {
46274                 tag: 'input',
46275                 id : id,
46276                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46277                 maxlength: this.max_length,
46278                 cls : 'form-control tel-input',
46279                 autocomplete: 'new-password'
46280             };
46281             
46282             var hiddenInput = {
46283                 tag: 'input',
46284                 type: 'hidden',
46285                 cls: 'hidden-tel-input'
46286             };
46287             
46288             if (this.name) {
46289                 hiddenInput.name = this.name;
46290             }
46291             
46292             if (this.disabled) {
46293                 input.disabled = true;
46294             }
46295             
46296             var flag_container = {
46297                 tag: 'div',
46298                 cls: 'flag-box',
46299                 cn: [
46300                     {
46301                         tag: 'div',
46302                         cls: 'flag'
46303                     },
46304                     {
46305                         tag: 'div',
46306                         cls: 'caret'
46307                     }
46308                 ]
46309             };
46310             
46311             var box = {
46312                 tag: 'div',
46313                 cls: this.hasFeedback ? 'has-feedback' : '',
46314                 cn: [
46315                     hiddenInput,
46316                     input,
46317                     {
46318                         tag: 'input',
46319                         cls: 'dial-code-holder',
46320                         disabled: true
46321                     }
46322                 ]
46323             };
46324             
46325             var container = {
46326                 cls: 'roo-select2-container input-group',
46327                 cn: [
46328                     flag_container,
46329                     box
46330                 ]
46331             };
46332             
46333             if (this.fieldLabel.length) {
46334                 var indicator = {
46335                     tag: 'i',
46336                     tooltip: 'This field is required'
46337                 };
46338                 
46339                 var label = {
46340                     tag: 'label',
46341                     'for':  id,
46342                     cls: 'control-label',
46343                     cn: []
46344                 };
46345                 
46346                 var label_text = {
46347                     tag: 'span',
46348                     html: this.fieldLabel
46349                 };
46350                 
46351                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46352                 label.cn = [
46353                     indicator,
46354                     label_text
46355                 ];
46356                 
46357                 if(this.indicatorpos == 'right') {
46358                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46359                     label.cn = [
46360                         label_text,
46361                         indicator
46362                     ];
46363                 }
46364                 
46365                 if(align == 'left') {
46366                     container = {
46367                         tag: 'div',
46368                         cn: [
46369                             container
46370                         ]
46371                     };
46372                     
46373                     if(this.labelWidth > 12){
46374                         label.style = "width: " + this.labelWidth + 'px';
46375                     }
46376                     if(this.labelWidth < 13 && this.labelmd == 0){
46377                         this.labelmd = this.labelWidth;
46378                     }
46379                     if(this.labellg > 0){
46380                         label.cls += ' col-lg-' + this.labellg;
46381                         input.cls += ' col-lg-' + (12 - this.labellg);
46382                     }
46383                     if(this.labelmd > 0){
46384                         label.cls += ' col-md-' + this.labelmd;
46385                         container.cls += ' col-md-' + (12 - this.labelmd);
46386                     }
46387                     if(this.labelsm > 0){
46388                         label.cls += ' col-sm-' + this.labelsm;
46389                         container.cls += ' col-sm-' + (12 - this.labelsm);
46390                     }
46391                     if(this.labelxs > 0){
46392                         label.cls += ' col-xs-' + this.labelxs;
46393                         container.cls += ' col-xs-' + (12 - this.labelxs);
46394                     }
46395                 }
46396             }
46397             
46398             cfg.cn = [
46399                 label,
46400                 container
46401             ];
46402             
46403             var settings = this;
46404             
46405             ['xs','sm','md','lg'].map(function(size){
46406                 if (settings[size]) {
46407                     cfg.cls += ' col-' + size + '-' + settings[size];
46408                 }
46409             });
46410             
46411             this.store = new Roo.data.Store({
46412                 proxy : new Roo.data.MemoryProxy({}),
46413                 reader : new Roo.data.JsonReader({
46414                     fields : [
46415                         {
46416                             'name' : 'name',
46417                             'type' : 'string'
46418                         },
46419                         {
46420                             'name' : 'iso2',
46421                             'type' : 'string'
46422                         },
46423                         {
46424                             'name' : 'dialCode',
46425                             'type' : 'string'
46426                         },
46427                         {
46428                             'name' : 'priority',
46429                             'type' : 'string'
46430                         },
46431                         {
46432                             'name' : 'areaCodes',
46433                             'type' : 'string'
46434                         }
46435                     ]
46436                 })
46437             });
46438             
46439             if(!this.preferedCountries) {
46440                 this.preferedCountries = [
46441                     'hk',
46442                     'gb',
46443                     'us'
46444                 ];
46445             }
46446             
46447             var p = this.preferedCountries.reverse();
46448             
46449             if(p) {
46450                 for (var i = 0; i < p.length; i++) {
46451                     for (var j = 0; j < this.allCountries.length; j++) {
46452                         if(this.allCountries[j].iso2 == p[i]) {
46453                             var t = this.allCountries[j];
46454                             this.allCountries.splice(j,1);
46455                             this.allCountries.unshift(t);
46456                         }
46457                     } 
46458                 }
46459             }
46460             
46461             this.store.proxy.data = {
46462                 success: true,
46463                 data: this.allCountries
46464             };
46465             
46466             return cfg;
46467         },
46468         
46469         initEvents : function()
46470         {
46471             this.createList();
46472             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46473             
46474             this.indicator = this.indicatorEl();
46475             this.flag = this.flagEl();
46476             this.dialCodeHolder = this.dialCodeHolderEl();
46477             
46478             this.trigger = this.el.select('div.flag-box',true).first();
46479             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46480             
46481             var _this = this;
46482             
46483             (function(){
46484                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46485                 _this.list.setWidth(lw);
46486             }).defer(100);
46487             
46488             this.list.on('mouseover', this.onViewOver, this);
46489             this.list.on('mousemove', this.onViewMove, this);
46490             this.inputEl().on("keyup", this.onKeyUp, this);
46491             this.inputEl().on("keypress", this.onKeyPress, this);
46492             
46493             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46494
46495             this.view = new Roo.View(this.list, this.tpl, {
46496                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46497             });
46498             
46499             this.view.on('click', this.onViewClick, this);
46500             this.setValue(this.defaultDialCode);
46501         },
46502         
46503         onTriggerClick : function(e)
46504         {
46505             Roo.log('trigger click');
46506             if(this.disabled){
46507                 return;
46508             }
46509             
46510             if(this.isExpanded()){
46511                 this.collapse();
46512                 this.hasFocus = false;
46513             }else {
46514                 this.store.load({});
46515                 this.hasFocus = true;
46516                 this.expand();
46517             }
46518         },
46519         
46520         isExpanded : function()
46521         {
46522             return this.list.isVisible();
46523         },
46524         
46525         collapse : function()
46526         {
46527             if(!this.isExpanded()){
46528                 return;
46529             }
46530             this.list.hide();
46531             Roo.get(document).un('mousedown', this.collapseIf, this);
46532             Roo.get(document).un('mousewheel', this.collapseIf, this);
46533             this.fireEvent('collapse', this);
46534             this.validate();
46535         },
46536         
46537         expand : function()
46538         {
46539             Roo.log('expand');
46540
46541             if(this.isExpanded() || !this.hasFocus){
46542                 return;
46543             }
46544             
46545             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46546             this.list.setWidth(lw);
46547             
46548             this.list.show();
46549             this.restrictHeight();
46550             
46551             Roo.get(document).on('mousedown', this.collapseIf, this);
46552             Roo.get(document).on('mousewheel', this.collapseIf, this);
46553             
46554             this.fireEvent('expand', this);
46555         },
46556         
46557         restrictHeight : function()
46558         {
46559             this.list.alignTo(this.inputEl(), this.listAlign);
46560             this.list.alignTo(this.inputEl(), this.listAlign);
46561         },
46562         
46563         onViewOver : function(e, t)
46564         {
46565             if(this.inKeyMode){
46566                 return;
46567             }
46568             var item = this.view.findItemFromChild(t);
46569             
46570             if(item){
46571                 var index = this.view.indexOf(item);
46572                 this.select(index, false);
46573             }
46574         },
46575
46576         // private
46577         onViewClick : function(view, doFocus, el, e)
46578         {
46579             var index = this.view.getSelectedIndexes()[0];
46580             
46581             var r = this.store.getAt(index);
46582             
46583             if(r){
46584                 this.onSelect(r, index);
46585             }
46586             if(doFocus !== false && !this.blockFocus){
46587                 this.inputEl().focus();
46588             }
46589         },
46590         
46591         onViewMove : function(e, t)
46592         {
46593             this.inKeyMode = false;
46594         },
46595         
46596         select : function(index, scrollIntoView)
46597         {
46598             this.selectedIndex = index;
46599             this.view.select(index);
46600             if(scrollIntoView !== false){
46601                 var el = this.view.getNode(index);
46602                 if(el){
46603                     this.list.scrollChildIntoView(el, false);
46604                 }
46605             }
46606         },
46607         
46608         createList : function()
46609         {
46610             this.list = Roo.get(document.body).createChild({
46611                 tag: 'ul',
46612                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46613                 style: 'display:none'
46614             });
46615             
46616             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46617         },
46618         
46619         collapseIf : function(e)
46620         {
46621             var in_combo  = e.within(this.el);
46622             var in_list =  e.within(this.list);
46623             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46624             
46625             if (in_combo || in_list || is_list) {
46626                 return;
46627             }
46628             this.collapse();
46629         },
46630         
46631         onSelect : function(record, index)
46632         {
46633             if(this.fireEvent('beforeselect', this, record, index) !== false){
46634                 
46635                 this.setFlagClass(record.data.iso2);
46636                 this.setDialCode(record.data.dialCode);
46637                 this.hasFocus = false;
46638                 this.collapse();
46639                 this.fireEvent('select', this, record, index);
46640             }
46641         },
46642         
46643         flagEl : function()
46644         {
46645             var flag = this.el.select('div.flag',true).first();
46646             if(!flag){
46647                 return false;
46648             }
46649             return flag;
46650         },
46651         
46652         dialCodeHolderEl : function()
46653         {
46654             var d = this.el.select('input.dial-code-holder',true).first();
46655             if(!d){
46656                 return false;
46657             }
46658             return d;
46659         },
46660         
46661         setDialCode : function(v)
46662         {
46663             this.dialCodeHolder.dom.value = '+'+v;
46664         },
46665         
46666         setFlagClass : function(n)
46667         {
46668             this.flag.dom.className = 'flag '+n;
46669         },
46670         
46671         getValue : function()
46672         {
46673             var v = this.inputEl().getValue();
46674             if(this.dialCodeHolder) {
46675                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46676             }
46677             return v;
46678         },
46679         
46680         setValue : function(v)
46681         {
46682             var d = this.getDialCode(v);
46683             
46684             //invalid dial code
46685             if(v.length == 0 || !d || d.length == 0) {
46686                 if(this.rendered){
46687                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46688                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46689                 }
46690                 return;
46691             }
46692             
46693             //valid dial code
46694             this.setFlagClass(this.dialCodeMapping[d].iso2);
46695             this.setDialCode(d);
46696             this.inputEl().dom.value = v.replace('+'+d,'');
46697             this.hiddenEl().dom.value = this.getValue();
46698             
46699             this.validate();
46700         },
46701         
46702         getDialCode : function(v)
46703         {
46704             v = v ||  '';
46705             
46706             if (v.length == 0) {
46707                 return this.dialCodeHolder.dom.value;
46708             }
46709             
46710             var dialCode = "";
46711             if (v.charAt(0) != "+") {
46712                 return false;
46713             }
46714             var numericChars = "";
46715             for (var i = 1; i < v.length; i++) {
46716               var c = v.charAt(i);
46717               if (!isNaN(c)) {
46718                 numericChars += c;
46719                 if (this.dialCodeMapping[numericChars]) {
46720                   dialCode = v.substr(1, i);
46721                 }
46722                 if (numericChars.length == 4) {
46723                   break;
46724                 }
46725               }
46726             }
46727             return dialCode;
46728         },
46729         
46730         reset : function()
46731         {
46732             this.setValue(this.defaultDialCode);
46733             this.validate();
46734         },
46735         
46736         hiddenEl : function()
46737         {
46738             return this.el.select('input.hidden-tel-input',true).first();
46739         },
46740         
46741         // after setting val
46742         onKeyUp : function(e){
46743             this.setValue(this.getValue());
46744         },
46745         
46746         onKeyPress : function(e){
46747             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46748                 e.stopEvent();
46749             }
46750         }
46751         
46752 });
46753 /**
46754  * @class Roo.bootstrap.form.MoneyField
46755  * @extends Roo.bootstrap.form.ComboBox
46756  * Bootstrap MoneyField class
46757  * 
46758  * @constructor
46759  * Create a new MoneyField.
46760  * @param {Object} config Configuration options
46761  */
46762
46763 Roo.bootstrap.form.MoneyField = function(config) {
46764     
46765     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46766     
46767 };
46768
46769 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46770     
46771     /**
46772      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46773      */
46774     allowDecimals : true,
46775     /**
46776      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46777      */
46778     decimalSeparator : ".",
46779     /**
46780      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46781      */
46782     decimalPrecision : 0,
46783     /**
46784      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46785      */
46786     allowNegative : true,
46787     /**
46788      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46789      */
46790     allowZero: true,
46791     /**
46792      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46793      */
46794     minValue : Number.NEGATIVE_INFINITY,
46795     /**
46796      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46797      */
46798     maxValue : Number.MAX_VALUE,
46799     /**
46800      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46801      */
46802     minText : "The minimum value for this field is {0}",
46803     /**
46804      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46805      */
46806     maxText : "The maximum value for this field is {0}",
46807     /**
46808      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46809      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46810      */
46811     nanText : "{0} is not a valid number",
46812     /**
46813      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46814      */
46815     castInt : true,
46816     /**
46817      * @cfg {String} defaults currency of the MoneyField
46818      * value should be in lkey
46819      */
46820     defaultCurrency : false,
46821     /**
46822      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46823      */
46824     thousandsDelimiter : false,
46825     /**
46826      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46827      */
46828     max_length: false,
46829     
46830     inputlg : 9,
46831     inputmd : 9,
46832     inputsm : 9,
46833     inputxs : 6,
46834      /**
46835      * @cfg {Roo.data.Store} store  Store to lookup currency??
46836      */
46837     store : false,
46838     
46839     getAutoCreate : function()
46840     {
46841         var align = this.labelAlign || this.parentLabelAlign();
46842         
46843         var id = Roo.id();
46844
46845         var cfg = {
46846             cls: 'form-group',
46847             cn: []
46848         };
46849
46850         var input =  {
46851             tag: 'input',
46852             id : id,
46853             cls : 'form-control roo-money-amount-input',
46854             autocomplete: 'new-password'
46855         };
46856         
46857         var hiddenInput = {
46858             tag: 'input',
46859             type: 'hidden',
46860             id: Roo.id(),
46861             cls: 'hidden-number-input'
46862         };
46863         
46864         if(this.max_length) {
46865             input.maxlength = this.max_length; 
46866         }
46867         
46868         if (this.name) {
46869             hiddenInput.name = this.name;
46870         }
46871
46872         if (this.disabled) {
46873             input.disabled = true;
46874         }
46875
46876         var clg = 12 - this.inputlg;
46877         var cmd = 12 - this.inputmd;
46878         var csm = 12 - this.inputsm;
46879         var cxs = 12 - this.inputxs;
46880         
46881         var container = {
46882             tag : 'div',
46883             cls : 'row roo-money-field',
46884             cn : [
46885                 {
46886                     tag : 'div',
46887                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46888                     cn : [
46889                         {
46890                             tag : 'div',
46891                             cls: 'roo-select2-container input-group',
46892                             cn: [
46893                                 {
46894                                     tag : 'input',
46895                                     cls : 'form-control roo-money-currency-input',
46896                                     autocomplete: 'new-password',
46897                                     readOnly : 1,
46898                                     name : this.currencyName
46899                                 },
46900                                 {
46901                                     tag :'span',
46902                                     cls : 'input-group-addon',
46903                                     cn : [
46904                                         {
46905                                             tag: 'span',
46906                                             cls: 'caret'
46907                                         }
46908                                     ]
46909                                 }
46910                             ]
46911                         }
46912                     ]
46913                 },
46914                 {
46915                     tag : 'div',
46916                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46917                     cn : [
46918                         {
46919                             tag: 'div',
46920                             cls: this.hasFeedback ? 'has-feedback' : '',
46921                             cn: [
46922                                 input
46923                             ]
46924                         }
46925                     ]
46926                 }
46927             ]
46928             
46929         };
46930         
46931         if (this.fieldLabel.length) {
46932             var indicator = {
46933                 tag: 'i',
46934                 tooltip: 'This field is required'
46935             };
46936
46937             var label = {
46938                 tag: 'label',
46939                 'for':  id,
46940                 cls: 'control-label',
46941                 cn: []
46942             };
46943
46944             var label_text = {
46945                 tag: 'span',
46946                 html: this.fieldLabel
46947             };
46948
46949             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46950             label.cn = [
46951                 indicator,
46952                 label_text
46953             ];
46954
46955             if(this.indicatorpos == 'right') {
46956                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46957                 label.cn = [
46958                     label_text,
46959                     indicator
46960                 ];
46961             }
46962
46963             if(align == 'left') {
46964                 container = {
46965                     tag: 'div',
46966                     cn: [
46967                         container
46968                     ]
46969                 };
46970
46971                 if(this.labelWidth > 12){
46972                     label.style = "width: " + this.labelWidth + 'px';
46973                 }
46974                 if(this.labelWidth < 13 && this.labelmd == 0){
46975                     this.labelmd = this.labelWidth;
46976                 }
46977                 if(this.labellg > 0){
46978                     label.cls += ' col-lg-' + this.labellg;
46979                     input.cls += ' col-lg-' + (12 - this.labellg);
46980                 }
46981                 if(this.labelmd > 0){
46982                     label.cls += ' col-md-' + this.labelmd;
46983                     container.cls += ' col-md-' + (12 - this.labelmd);
46984                 }
46985                 if(this.labelsm > 0){
46986                     label.cls += ' col-sm-' + this.labelsm;
46987                     container.cls += ' col-sm-' + (12 - this.labelsm);
46988                 }
46989                 if(this.labelxs > 0){
46990                     label.cls += ' col-xs-' + this.labelxs;
46991                     container.cls += ' col-xs-' + (12 - this.labelxs);
46992                 }
46993             }
46994         }
46995
46996         cfg.cn = [
46997             label,
46998             container,
46999             hiddenInput
47000         ];
47001         
47002         var settings = this;
47003
47004         ['xs','sm','md','lg'].map(function(size){
47005             if (settings[size]) {
47006                 cfg.cls += ' col-' + size + '-' + settings[size];
47007             }
47008         });
47009         
47010         return cfg;
47011     },
47012     
47013     initEvents : function()
47014     {
47015         this.indicator = this.indicatorEl();
47016         
47017         this.initCurrencyEvent();
47018         
47019         this.initNumberEvent();
47020     },
47021     
47022     initCurrencyEvent : function()
47023     {
47024         if (!this.store) {
47025             throw "can not find store for combo";
47026         }
47027         
47028         this.store = Roo.factory(this.store, Roo.data);
47029         this.store.parent = this;
47030         
47031         this.createList();
47032         
47033         this.triggerEl = this.el.select('.input-group-addon', true).first();
47034         
47035         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47036         
47037         var _this = this;
47038         
47039         (function(){
47040             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47041             _this.list.setWidth(lw);
47042         }).defer(100);
47043         
47044         this.list.on('mouseover', this.onViewOver, this);
47045         this.list.on('mousemove', this.onViewMove, this);
47046         this.list.on('scroll', this.onViewScroll, this);
47047         
47048         if(!this.tpl){
47049             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47050         }
47051         
47052         this.view = new Roo.View(this.list, this.tpl, {
47053             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47054         });
47055         
47056         this.view.on('click', this.onViewClick, this);
47057         
47058         this.store.on('beforeload', this.onBeforeLoad, this);
47059         this.store.on('load', this.onLoad, this);
47060         this.store.on('loadexception', this.onLoadException, this);
47061         
47062         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47063             "up" : function(e){
47064                 this.inKeyMode = true;
47065                 this.selectPrev();
47066             },
47067
47068             "down" : function(e){
47069                 if(!this.isExpanded()){
47070                     this.onTriggerClick();
47071                 }else{
47072                     this.inKeyMode = true;
47073                     this.selectNext();
47074                 }
47075             },
47076
47077             "enter" : function(e){
47078                 this.collapse();
47079                 
47080                 if(this.fireEvent("specialkey", this, e)){
47081                     this.onViewClick(false);
47082                 }
47083                 
47084                 return true;
47085             },
47086
47087             "esc" : function(e){
47088                 this.collapse();
47089             },
47090
47091             "tab" : function(e){
47092                 this.collapse();
47093                 
47094                 if(this.fireEvent("specialkey", this, e)){
47095                     this.onViewClick(false);
47096                 }
47097                 
47098                 return true;
47099             },
47100
47101             scope : this,
47102
47103             doRelay : function(foo, bar, hname){
47104                 if(hname == 'down' || this.scope.isExpanded()){
47105                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47106                 }
47107                 return true;
47108             },
47109
47110             forceKeyDown: true
47111         });
47112         
47113         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47114         
47115     },
47116     
47117     initNumberEvent : function(e)
47118     {
47119         this.inputEl().on("keydown" , this.fireKey,  this);
47120         this.inputEl().on("focus", this.onFocus,  this);
47121         this.inputEl().on("blur", this.onBlur,  this);
47122         
47123         this.inputEl().relayEvent('keyup', this);
47124         
47125         if(this.indicator){
47126             this.indicator.addClass('invisible');
47127         }
47128  
47129         this.originalValue = this.getValue();
47130         
47131         if(this.validationEvent == 'keyup'){
47132             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47133             this.inputEl().on('keyup', this.filterValidation, this);
47134         }
47135         else if(this.validationEvent !== false){
47136             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47137         }
47138         
47139         if(this.selectOnFocus){
47140             this.on("focus", this.preFocus, this);
47141             
47142         }
47143         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47144             this.inputEl().on("keypress", this.filterKeys, this);
47145         } else {
47146             this.inputEl().relayEvent('keypress', this);
47147         }
47148         
47149         var allowed = "0123456789";
47150         
47151         if(this.allowDecimals){
47152             allowed += this.decimalSeparator;
47153         }
47154         
47155         if(this.allowNegative){
47156             allowed += "-";
47157         }
47158         
47159         if(this.thousandsDelimiter) {
47160             allowed += ",";
47161         }
47162         
47163         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47164         
47165         var keyPress = function(e){
47166             
47167             var k = e.getKey();
47168             
47169             var c = e.getCharCode();
47170             
47171             if(
47172                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47173                     allowed.indexOf(String.fromCharCode(c)) === -1
47174             ){
47175                 e.stopEvent();
47176                 return;
47177             }
47178             
47179             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47180                 return;
47181             }
47182             
47183             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47184                 e.stopEvent();
47185             }
47186         };
47187         
47188         this.inputEl().on("keypress", keyPress, this);
47189         
47190     },
47191     
47192     onTriggerClick : function(e)
47193     {   
47194         if(this.disabled){
47195             return;
47196         }
47197         
47198         this.page = 0;
47199         this.loadNext = false;
47200         
47201         if(this.isExpanded()){
47202             this.collapse();
47203             return;
47204         }
47205         
47206         this.hasFocus = true;
47207         
47208         if(this.triggerAction == 'all') {
47209             this.doQuery(this.allQuery, true);
47210             return;
47211         }
47212         
47213         this.doQuery(this.getRawValue());
47214     },
47215     
47216     getCurrency : function()
47217     {   
47218         var v = this.currencyEl().getValue();
47219         
47220         return v;
47221     },
47222     
47223     restrictHeight : function()
47224     {
47225         this.list.alignTo(this.currencyEl(), this.listAlign);
47226         this.list.alignTo(this.currencyEl(), this.listAlign);
47227     },
47228     
47229     onViewClick : function(view, doFocus, el, e)
47230     {
47231         var index = this.view.getSelectedIndexes()[0];
47232         
47233         var r = this.store.getAt(index);
47234         
47235         if(r){
47236             this.onSelect(r, index);
47237         }
47238     },
47239     
47240     onSelect : function(record, index){
47241         
47242         if(this.fireEvent('beforeselect', this, record, index) !== false){
47243         
47244             this.setFromCurrencyData(index > -1 ? record.data : false);
47245             
47246             this.collapse();
47247             
47248             this.fireEvent('select', this, record, index);
47249         }
47250     },
47251     
47252     setFromCurrencyData : function(o)
47253     {
47254         var currency = '';
47255         
47256         this.lastCurrency = o;
47257         
47258         if (this.currencyField) {
47259             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47260         } else {
47261             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47262         }
47263         
47264         this.lastSelectionText = currency;
47265         
47266         //setting default currency
47267         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47268             this.setCurrency(this.defaultCurrency);
47269             return;
47270         }
47271         
47272         this.setCurrency(currency);
47273     },
47274     
47275     setFromData : function(o)
47276     {
47277         var c = {};
47278         
47279         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47280         
47281         this.setFromCurrencyData(c);
47282         
47283         var value = '';
47284         
47285         if (this.name) {
47286             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47287         } else {
47288             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47289         }
47290         
47291         this.setValue(value);
47292         
47293     },
47294     
47295     setCurrency : function(v)
47296     {   
47297         this.currencyValue = v;
47298         
47299         if(this.rendered){
47300             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47301             this.validate();
47302         }
47303     },
47304     
47305     setValue : function(v)
47306     {
47307         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47308         
47309         this.value = v;
47310         
47311         if(this.rendered){
47312             
47313             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47314             
47315             this.inputEl().dom.value = (v == '') ? '' :
47316                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47317             
47318             if(!this.allowZero && v === '0') {
47319                 this.hiddenEl().dom.value = '';
47320                 this.inputEl().dom.value = '';
47321             }
47322             
47323             this.validate();
47324         }
47325     },
47326     
47327     getRawValue : function()
47328     {
47329         var v = this.inputEl().getValue();
47330         
47331         return v;
47332     },
47333     
47334     getValue : function()
47335     {
47336         return this.fixPrecision(this.parseValue(this.getRawValue()));
47337     },
47338     
47339     parseValue : function(value)
47340     {
47341         if(this.thousandsDelimiter) {
47342             value += "";
47343             r = new RegExp(",", "g");
47344             value = value.replace(r, "");
47345         }
47346         
47347         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47348         return isNaN(value) ? '' : value;
47349         
47350     },
47351     
47352     fixPrecision : function(value)
47353     {
47354         if(this.thousandsDelimiter) {
47355             value += "";
47356             r = new RegExp(",", "g");
47357             value = value.replace(r, "");
47358         }
47359         
47360         var nan = isNaN(value);
47361         
47362         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47363             return nan ? '' : value;
47364         }
47365         return parseFloat(value).toFixed(this.decimalPrecision);
47366     },
47367     
47368     decimalPrecisionFcn : function(v)
47369     {
47370         return Math.floor(v);
47371     },
47372     
47373     validateValue : function(value)
47374     {
47375         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47376             return false;
47377         }
47378         
47379         var num = this.parseValue(value);
47380         
47381         if(isNaN(num)){
47382             this.markInvalid(String.format(this.nanText, value));
47383             return false;
47384         }
47385         
47386         if(num < this.minValue){
47387             this.markInvalid(String.format(this.minText, this.minValue));
47388             return false;
47389         }
47390         
47391         if(num > this.maxValue){
47392             this.markInvalid(String.format(this.maxText, this.maxValue));
47393             return false;
47394         }
47395         
47396         return true;
47397     },
47398     
47399     validate : function()
47400     {
47401         if(this.disabled || this.allowBlank){
47402             this.markValid();
47403             return true;
47404         }
47405         
47406         var currency = this.getCurrency();
47407         
47408         if(this.validateValue(this.getRawValue()) && currency.length){
47409             this.markValid();
47410             return true;
47411         }
47412         
47413         this.markInvalid();
47414         return false;
47415     },
47416     
47417     getName: function()
47418     {
47419         return this.name;
47420     },
47421     
47422     beforeBlur : function()
47423     {
47424         if(!this.castInt){
47425             return;
47426         }
47427         
47428         var v = this.parseValue(this.getRawValue());
47429         
47430         if(v || v == 0){
47431             this.setValue(v);
47432         }
47433     },
47434     
47435     onBlur : function()
47436     {
47437         this.beforeBlur();
47438         
47439         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47440             //this.el.removeClass(this.focusClass);
47441         }
47442         
47443         this.hasFocus = false;
47444         
47445         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47446             this.validate();
47447         }
47448         
47449         var v = this.getValue();
47450         
47451         if(String(v) !== String(this.startValue)){
47452             this.fireEvent('change', this, v, this.startValue);
47453         }
47454         
47455         this.fireEvent("blur", this);
47456     },
47457     
47458     inputEl : function()
47459     {
47460         return this.el.select('.roo-money-amount-input', true).first();
47461     },
47462     
47463     currencyEl : function()
47464     {
47465         return this.el.select('.roo-money-currency-input', true).first();
47466     },
47467     
47468     hiddenEl : function()
47469     {
47470         return this.el.select('input.hidden-number-input',true).first();
47471     }
47472     
47473 });/**
47474  * @class Roo.bootstrap.BezierSignature
47475  * @extends Roo.bootstrap.Component
47476  * Bootstrap BezierSignature class
47477  * This script refer to:
47478  *    Title: Signature Pad
47479  *    Author: szimek
47480  *    Availability: https://github.com/szimek/signature_pad
47481  *
47482  * @constructor
47483  * Create a new BezierSignature
47484  * @param {Object} config The config object
47485  */
47486
47487 Roo.bootstrap.BezierSignature = function(config){
47488     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47489     this.addEvents({
47490         "resize" : true
47491     });
47492 };
47493
47494 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47495 {
47496      
47497     curve_data: [],
47498     
47499     is_empty: true,
47500     
47501     mouse_btn_down: true,
47502     
47503     /**
47504      * @cfg {int} canvas height
47505      */
47506     canvas_height: '200px',
47507     
47508     /**
47509      * @cfg {float|function} Radius of a single dot.
47510      */ 
47511     dot_size: false,
47512     
47513     /**
47514      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47515      */
47516     min_width: 0.5,
47517     
47518     /**
47519      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47520      */
47521     max_width: 2.5,
47522     
47523     /**
47524      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47525      */
47526     throttle: 16,
47527     
47528     /**
47529      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47530      */
47531     min_distance: 5,
47532     
47533     /**
47534      * @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.
47535      */
47536     bg_color: 'rgba(0, 0, 0, 0)',
47537     
47538     /**
47539      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47540      */
47541     dot_color: 'black',
47542     
47543     /**
47544      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47545      */ 
47546     velocity_filter_weight: 0.7,
47547     
47548     /**
47549      * @cfg {function} Callback when stroke begin. 
47550      */
47551     onBegin: false,
47552     
47553     /**
47554      * @cfg {function} Callback when stroke end.
47555      */
47556     onEnd: false,
47557     
47558     getAutoCreate : function()
47559     {
47560         var cls = 'roo-signature column';
47561         
47562         if(this.cls){
47563             cls += ' ' + this.cls;
47564         }
47565         
47566         var col_sizes = [
47567             'lg',
47568             'md',
47569             'sm',
47570             'xs'
47571         ];
47572         
47573         for(var i = 0; i < col_sizes.length; i++) {
47574             if(this[col_sizes[i]]) {
47575                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47576             }
47577         }
47578         
47579         var cfg = {
47580             tag: 'div',
47581             cls: cls,
47582             cn: [
47583                 {
47584                     tag: 'div',
47585                     cls: 'roo-signature-body',
47586                     cn: [
47587                         {
47588                             tag: 'canvas',
47589                             cls: 'roo-signature-body-canvas',
47590                             height: this.canvas_height,
47591                             width: this.canvas_width
47592                         }
47593                     ]
47594                 },
47595                 {
47596                     tag: 'input',
47597                     type: 'file',
47598                     style: 'display: none'
47599                 }
47600             ]
47601         };
47602         
47603         return cfg;
47604     },
47605     
47606     initEvents: function() 
47607     {
47608         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47609         
47610         var canvas = this.canvasEl();
47611         
47612         // mouse && touch event swapping...
47613         canvas.dom.style.touchAction = 'none';
47614         canvas.dom.style.msTouchAction = 'none';
47615         
47616         this.mouse_btn_down = false;
47617         canvas.on('mousedown', this._handleMouseDown, this);
47618         canvas.on('mousemove', this._handleMouseMove, this);
47619         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47620         
47621         if (window.PointerEvent) {
47622             canvas.on('pointerdown', this._handleMouseDown, this);
47623             canvas.on('pointermove', this._handleMouseMove, this);
47624             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47625         }
47626         
47627         if ('ontouchstart' in window) {
47628             canvas.on('touchstart', this._handleTouchStart, this);
47629             canvas.on('touchmove', this._handleTouchMove, this);
47630             canvas.on('touchend', this._handleTouchEnd, this);
47631         }
47632         
47633         Roo.EventManager.onWindowResize(this.resize, this, true);
47634         
47635         // file input event
47636         this.fileEl().on('change', this.uploadImage, this);
47637         
47638         this.clear();
47639         
47640         this.resize();
47641     },
47642     
47643     resize: function(){
47644         
47645         var canvas = this.canvasEl().dom;
47646         var ctx = this.canvasElCtx();
47647         var img_data = false;
47648         
47649         if(canvas.width > 0) {
47650             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47651         }
47652         // setting canvas width will clean img data
47653         canvas.width = 0;
47654         
47655         var style = window.getComputedStyle ? 
47656             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47657             
47658         var padding_left = parseInt(style.paddingLeft) || 0;
47659         var padding_right = parseInt(style.paddingRight) || 0;
47660         
47661         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47662         
47663         if(img_data) {
47664             ctx.putImageData(img_data, 0, 0);
47665         }
47666     },
47667     
47668     _handleMouseDown: function(e)
47669     {
47670         if (e.browserEvent.which === 1) {
47671             this.mouse_btn_down = true;
47672             this.strokeBegin(e);
47673         }
47674     },
47675     
47676     _handleMouseMove: function (e)
47677     {
47678         if (this.mouse_btn_down) {
47679             this.strokeMoveUpdate(e);
47680         }
47681     },
47682     
47683     _handleMouseUp: function (e)
47684     {
47685         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47686             this.mouse_btn_down = false;
47687             this.strokeEnd(e);
47688         }
47689     },
47690     
47691     _handleTouchStart: function (e) {
47692         
47693         e.preventDefault();
47694         if (e.browserEvent.targetTouches.length === 1) {
47695             // var touch = e.browserEvent.changedTouches[0];
47696             // this.strokeBegin(touch);
47697             
47698              this.strokeBegin(e); // assume e catching the correct xy...
47699         }
47700     },
47701     
47702     _handleTouchMove: function (e) {
47703         e.preventDefault();
47704         // var touch = event.targetTouches[0];
47705         // _this._strokeMoveUpdate(touch);
47706         this.strokeMoveUpdate(e);
47707     },
47708     
47709     _handleTouchEnd: function (e) {
47710         var wasCanvasTouched = e.target === this.canvasEl().dom;
47711         if (wasCanvasTouched) {
47712             e.preventDefault();
47713             // var touch = event.changedTouches[0];
47714             // _this._strokeEnd(touch);
47715             this.strokeEnd(e);
47716         }
47717     },
47718     
47719     reset: function () {
47720         this._lastPoints = [];
47721         this._lastVelocity = 0;
47722         this._lastWidth = (this.min_width + this.max_width) / 2;
47723         this.canvasElCtx().fillStyle = this.dot_color;
47724     },
47725     
47726     strokeMoveUpdate: function(e)
47727     {
47728         this.strokeUpdate(e);
47729         
47730         if (this.throttle) {
47731             this.throttleStroke(this.strokeUpdate, this.throttle);
47732         }
47733         else {
47734             this.strokeUpdate(e);
47735         }
47736     },
47737     
47738     strokeBegin: function(e)
47739     {
47740         var newPointGroup = {
47741             color: this.dot_color,
47742             points: []
47743         };
47744         
47745         if (typeof this.onBegin === 'function') {
47746             this.onBegin(e);
47747         }
47748         
47749         this.curve_data.push(newPointGroup);
47750         this.reset();
47751         this.strokeUpdate(e);
47752     },
47753     
47754     strokeUpdate: function(e)
47755     {
47756         var rect = this.canvasEl().dom.getBoundingClientRect();
47757         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47758         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47759         var lastPoints = lastPointGroup.points;
47760         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47761         var isLastPointTooClose = lastPoint
47762             ? point.distanceTo(lastPoint) <= this.min_distance
47763             : false;
47764         var color = lastPointGroup.color;
47765         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47766             var curve = this.addPoint(point);
47767             if (!lastPoint) {
47768                 this.drawDot({color: color, point: point});
47769             }
47770             else if (curve) {
47771                 this.drawCurve({color: color, curve: curve});
47772             }
47773             lastPoints.push({
47774                 time: point.time,
47775                 x: point.x,
47776                 y: point.y
47777             });
47778         }
47779     },
47780     
47781     strokeEnd: function(e)
47782     {
47783         this.strokeUpdate(e);
47784         if (typeof this.onEnd === 'function') {
47785             this.onEnd(e);
47786         }
47787     },
47788     
47789     addPoint:  function (point) {
47790         var _lastPoints = this._lastPoints;
47791         _lastPoints.push(point);
47792         if (_lastPoints.length > 2) {
47793             if (_lastPoints.length === 3) {
47794                 _lastPoints.unshift(_lastPoints[0]);
47795             }
47796             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47797             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47798             _lastPoints.shift();
47799             return curve;
47800         }
47801         return null;
47802     },
47803     
47804     calculateCurveWidths: function (startPoint, endPoint) {
47805         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47806             (1 - this.velocity_filter_weight) * this._lastVelocity;
47807
47808         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47809         var widths = {
47810             end: newWidth,
47811             start: this._lastWidth
47812         };
47813         
47814         this._lastVelocity = velocity;
47815         this._lastWidth = newWidth;
47816         return widths;
47817     },
47818     
47819     drawDot: function (_a) {
47820         var color = _a.color, point = _a.point;
47821         var ctx = this.canvasElCtx();
47822         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47823         ctx.beginPath();
47824         this.drawCurveSegment(point.x, point.y, width);
47825         ctx.closePath();
47826         ctx.fillStyle = color;
47827         ctx.fill();
47828     },
47829     
47830     drawCurve: function (_a) {
47831         var color = _a.color, curve = _a.curve;
47832         var ctx = this.canvasElCtx();
47833         var widthDelta = curve.endWidth - curve.startWidth;
47834         var drawSteps = Math.floor(curve.length()) * 2;
47835         ctx.beginPath();
47836         ctx.fillStyle = color;
47837         for (var i = 0; i < drawSteps; i += 1) {
47838         var t = i / drawSteps;
47839         var tt = t * t;
47840         var ttt = tt * t;
47841         var u = 1 - t;
47842         var uu = u * u;
47843         var uuu = uu * u;
47844         var x = uuu * curve.startPoint.x;
47845         x += 3 * uu * t * curve.control1.x;
47846         x += 3 * u * tt * curve.control2.x;
47847         x += ttt * curve.endPoint.x;
47848         var y = uuu * curve.startPoint.y;
47849         y += 3 * uu * t * curve.control1.y;
47850         y += 3 * u * tt * curve.control2.y;
47851         y += ttt * curve.endPoint.y;
47852         var width = curve.startWidth + ttt * widthDelta;
47853         this.drawCurveSegment(x, y, width);
47854         }
47855         ctx.closePath();
47856         ctx.fill();
47857     },
47858     
47859     drawCurveSegment: function (x, y, width) {
47860         var ctx = this.canvasElCtx();
47861         ctx.moveTo(x, y);
47862         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47863         this.is_empty = false;
47864     },
47865     
47866     clear: function()
47867     {
47868         var ctx = this.canvasElCtx();
47869         var canvas = this.canvasEl().dom;
47870         ctx.fillStyle = this.bg_color;
47871         ctx.clearRect(0, 0, canvas.width, canvas.height);
47872         ctx.fillRect(0, 0, canvas.width, canvas.height);
47873         this.curve_data = [];
47874         this.reset();
47875         this.is_empty = true;
47876     },
47877     
47878     fileEl: function()
47879     {
47880         return  this.el.select('input',true).first();
47881     },
47882     
47883     canvasEl: function()
47884     {
47885         return this.el.select('canvas',true).first();
47886     },
47887     
47888     canvasElCtx: function()
47889     {
47890         return this.el.select('canvas',true).first().dom.getContext('2d');
47891     },
47892     
47893     getImage: function(type)
47894     {
47895         if(this.is_empty) {
47896             return false;
47897         }
47898         
47899         // encryption ?
47900         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47901     },
47902     
47903     drawFromImage: function(img_src)
47904     {
47905         var img = new Image();
47906         
47907         img.onload = function(){
47908             this.canvasElCtx().drawImage(img, 0, 0);
47909         }.bind(this);
47910         
47911         img.src = img_src;
47912         
47913         this.is_empty = false;
47914     },
47915     
47916     selectImage: function()
47917     {
47918         this.fileEl().dom.click();
47919     },
47920     
47921     uploadImage: function(e)
47922     {
47923         var reader = new FileReader();
47924         
47925         reader.onload = function(e){
47926             var img = new Image();
47927             img.onload = function(){
47928                 this.reset();
47929                 this.canvasElCtx().drawImage(img, 0, 0);
47930             }.bind(this);
47931             img.src = e.target.result;
47932         }.bind(this);
47933         
47934         reader.readAsDataURL(e.target.files[0]);
47935     },
47936     
47937     // Bezier Point Constructor
47938     Point: (function () {
47939         function Point(x, y, time) {
47940             this.x = x;
47941             this.y = y;
47942             this.time = time || Date.now();
47943         }
47944         Point.prototype.distanceTo = function (start) {
47945             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47946         };
47947         Point.prototype.equals = function (other) {
47948             return this.x === other.x && this.y === other.y && this.time === other.time;
47949         };
47950         Point.prototype.velocityFrom = function (start) {
47951             return this.time !== start.time
47952             ? this.distanceTo(start) / (this.time - start.time)
47953             : 0;
47954         };
47955         return Point;
47956     }()),
47957     
47958     
47959     // Bezier Constructor
47960     Bezier: (function () {
47961         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47962             this.startPoint = startPoint;
47963             this.control2 = control2;
47964             this.control1 = control1;
47965             this.endPoint = endPoint;
47966             this.startWidth = startWidth;
47967             this.endWidth = endWidth;
47968         }
47969         Bezier.fromPoints = function (points, widths, scope) {
47970             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47971             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47972             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47973         };
47974         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47975             var dx1 = s1.x - s2.x;
47976             var dy1 = s1.y - s2.y;
47977             var dx2 = s2.x - s3.x;
47978             var dy2 = s2.y - s3.y;
47979             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47980             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47981             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47982             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47983             var dxm = m1.x - m2.x;
47984             var dym = m1.y - m2.y;
47985             var k = l2 / (l1 + l2);
47986             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47987             var tx = s2.x - cm.x;
47988             var ty = s2.y - cm.y;
47989             return {
47990                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47991                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47992             };
47993         };
47994         Bezier.prototype.length = function () {
47995             var steps = 10;
47996             var length = 0;
47997             var px;
47998             var py;
47999             for (var i = 0; i <= steps; i += 1) {
48000                 var t = i / steps;
48001                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
48002                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
48003                 if (i > 0) {
48004                     var xdiff = cx - px;
48005                     var ydiff = cy - py;
48006                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48007                 }
48008                 px = cx;
48009                 py = cy;
48010             }
48011             return length;
48012         };
48013         Bezier.prototype.point = function (t, start, c1, c2, end) {
48014             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48015             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48016             + (3.0 * c2 * (1.0 - t) * t * t)
48017             + (end * t * t * t);
48018         };
48019         return Bezier;
48020     }()),
48021     
48022     throttleStroke: function(fn, wait) {
48023       if (wait === void 0) { wait = 250; }
48024       var previous = 0;
48025       var timeout = null;
48026       var result;
48027       var storedContext;
48028       var storedArgs;
48029       var later = function () {
48030           previous = Date.now();
48031           timeout = null;
48032           result = fn.apply(storedContext, storedArgs);
48033           if (!timeout) {
48034               storedContext = null;
48035               storedArgs = [];
48036           }
48037       };
48038       return function wrapper() {
48039           var args = [];
48040           for (var _i = 0; _i < arguments.length; _i++) {
48041               args[_i] = arguments[_i];
48042           }
48043           var now = Date.now();
48044           var remaining = wait - (now - previous);
48045           storedContext = this;
48046           storedArgs = args;
48047           if (remaining <= 0 || remaining > wait) {
48048               if (timeout) {
48049                   clearTimeout(timeout);
48050                   timeout = null;
48051               }
48052               previous = now;
48053               result = fn.apply(storedContext, storedArgs);
48054               if (!timeout) {
48055                   storedContext = null;
48056                   storedArgs = [];
48057               }
48058           }
48059           else if (!timeout) {
48060               timeout = window.setTimeout(later, remaining);
48061           }
48062           return result;
48063       };
48064   }
48065   
48066 });
48067
48068  
48069
48070  // old names for form elements
48071 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48072 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48073 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48074 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48075 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48076 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48077 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48078 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48079 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48080 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48081 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48082 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48083 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48084 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48085 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48086 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48087 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48088 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48089 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48090 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48091 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48092 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48093 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48094 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48095 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48096 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48097
48098 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48099 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48100
48101 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48102 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48103
48104 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48105 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48106 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48107 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48108